Fix bug where the cast cache would insert with wrong key.
[luabind.git] / src / inheritance.cpp
blob2e2ec902aadb1bc42fe51fce9ec8e2f075609558
1 // Copyright Daniel Wallin 2009. Use, modification and distribution is
2 // subject to the Boost Software License, Version 1.0. (See accompanying
3 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 #define LUABIND_BUILDING
7 #include <limits>
8 #include <map>
9 #include <vector>
10 #include <queue>
11 #include <boost/dynamic_bitset.hpp>
12 #include <boost/foreach.hpp>
13 #include <boost/tuple/tuple.hpp>
14 #include <boost/tuple/tuple_comparison.hpp>
15 #include <luabind/typeid.hpp>
16 #include <luabind/detail/inheritance.hpp>
18 namespace luabind { namespace detail {
20 class_id const class_id_map::local_id_base =
21 std::numeric_limits<class_id>::max() / 2;
23 namespace
26 struct edge
28 edge(class_id target, cast_function cast)
29 : target(target)
30 , cast(cast)
33 class_id target;
34 cast_function cast;
37 bool operator<(edge const& x, edge const& y)
39 return x.target < y.target;
42 struct vertex
44 vertex(class_id id)
45 : id(id)
48 class_id id;
49 std::vector<edge> edges;
52 typedef std::pair<std::ptrdiff_t, int> cache_entry;
54 class cache
56 public:
57 static std::ptrdiff_t const unknown;
58 static std::ptrdiff_t const invalid;
60 cache_entry get(
61 class_id src, class_id target, class_id dynamic_id
62 , std::ptrdiff_t object_offset) const;
64 void put(
65 class_id src, class_id target, class_id dynamic_id
66 , std::ptrdiff_t object_offset
67 , std::size_t distance, std::ptrdiff_t offset);
69 void invalidate();
71 private:
72 typedef boost::tuple<
73 class_id, class_id, class_id, std::ptrdiff_t> key_type;
74 typedef std::map<key_type, cache_entry> map_type;
75 map_type m_cache;
78 std::ptrdiff_t const cache::unknown =
79 std::numeric_limits<std::ptrdiff_t>::max();
80 std::ptrdiff_t const cache::invalid = cache::unknown - 1;
82 cache_entry cache::get(
83 class_id src, class_id target, class_id dynamic_id
84 , std::ptrdiff_t object_offset) const
86 map_type::const_iterator i = m_cache.find(
87 key_type(src, target, dynamic_id, object_offset));
88 return i != m_cache.end() ? i->second : cache_entry(unknown, -1);
91 void cache::put(
92 class_id src, class_id target, class_id dynamic_id
93 , std::ptrdiff_t object_offset, std::size_t distance, std::ptrdiff_t offset)
95 m_cache.insert(std::make_pair(
96 key_type(src, target, dynamic_id, object_offset)
97 , cache_entry(offset, distance)
98 ));
101 void cache::invalidate()
103 m_cache.clear();
106 } // namespace unnamed
108 class cast_graph::impl
110 public:
111 std::pair<void*, int> cast(
112 void* p, class_id src, class_id target
113 , class_id dynamic_id, void const* dynamic_ptr) const;
114 void insert(class_id src, class_id target, cast_function cast);
116 private:
117 std::vector<vertex> m_vertices;
118 mutable cache m_cache;
121 namespace
124 struct queue_entry
126 queue_entry(void* p, class_id vertex_id, int distance)
127 : p(p)
128 , vertex_id(vertex_id)
129 , distance(distance)
132 void* p;
133 class_id vertex_id;
134 int distance;
137 } // namespace unnamed
139 std::pair<void*, int> cast_graph::impl::cast(
140 void* const p, class_id src, class_id target
141 , class_id dynamic_id, void const* dynamic_ptr) const
143 if (src == target)
144 return std::make_pair(p, 0);
146 if (src >= m_vertices.size() || target >= m_vertices.size())
147 return std::pair<void*, int>((void*)0, -1);
149 std::ptrdiff_t const object_offset =
150 (char const*)dynamic_ptr - (char const*)p;
152 cache_entry cached = m_cache.get(src, target, dynamic_id, object_offset);
154 if (cached.first != cache::unknown)
156 if (cached.first == cache::invalid)
157 return std::pair<void*, int>((void*)0, -1);
158 return std::make_pair((char*)p + cached.first, cached.second);
161 std::queue<queue_entry> q;
162 q.push(queue_entry(p, src, 0));
164 boost::dynamic_bitset<> visited(m_vertices.size());
166 while (!q.empty())
168 queue_entry const qe = q.front();
169 q.pop();
171 visited[qe.vertex_id] = true;
172 vertex const& v = m_vertices[qe.vertex_id];
174 if (v.id == target)
176 m_cache.put(
177 src, target, dynamic_id, object_offset
178 , qe.distance, (char*)qe.p - (char*)p
181 return std::make_pair(qe.p, qe.distance);
184 BOOST_FOREACH(edge const& e, v.edges)
186 if (visited[e.target])
187 continue;
188 if (void* casted = e.cast(qe.p))
189 q.push(queue_entry(casted, e.target, qe.distance + 1));
193 m_cache.put(src, target, dynamic_id, object_offset, cache::invalid, -1);
195 return std::pair<void*, int>((void*)0, -1);
198 void cast_graph::impl::insert(
199 class_id src, class_id target, cast_function cast)
201 class_id const max_id = std::max(src, target);
203 if (max_id >= m_vertices.size())
205 m_vertices.reserve(max_id + 1);
206 for (class_id i = m_vertices.size(); i < max_id + 1; ++i)
207 m_vertices.push_back(vertex(i));
210 std::vector<edge>& edges = m_vertices[src].edges;
212 std::vector<edge>::iterator i = std::lower_bound(
213 edges.begin(), edges.end(), edge(target, 0)
216 if (i == edges.end() || i->target != target)
218 edges.insert(i, edge(target, cast));
219 m_cache.invalidate();
223 std::pair<void*, int> cast_graph::cast(
224 void* p, class_id src, class_id target
225 , class_id dynamic_id, void const* dynamic_ptr) const
227 return m_impl->cast(p, src, target, dynamic_id, dynamic_ptr);
230 void cast_graph::insert(class_id src, class_id target, cast_function cast)
232 m_impl->insert(src, target, cast);
235 cast_graph::cast_graph()
236 : m_impl(new impl)
239 cast_graph::~cast_graph()
242 LUABIND_API class_id allocate_class_id(type_id const& cls)
244 typedef std::map<type_id, class_id> map_type;
246 static map_type registered;
247 static class_id id = 0;
249 std::pair<map_type::iterator, bool> inserted = registered.insert(
250 std::make_pair(cls, id));
252 if (inserted.second)
253 ++id;
255 return inserted.first->second;
258 }} // namespace luabind::detail