Chris@101
|
1 /* Copyright 2003-2014 Joaquin M Lopez Munoz.
|
Chris@16
|
2 * Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
3 * (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
4 * http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
5 *
|
Chris@16
|
6 * See http://www.boost.org/libs/multi_index for library home page.
|
Chris@16
|
7 */
|
Chris@16
|
8
|
Chris@16
|
9 #ifndef BOOST_MULTI_INDEX_DETAIL_HASH_INDEX_NODE_HPP
|
Chris@16
|
10 #define BOOST_MULTI_INDEX_DETAIL_HASH_INDEX_NODE_HPP
|
Chris@16
|
11
|
Chris@101
|
12 #if defined(_MSC_VER)
|
Chris@16
|
13 #pragma once
|
Chris@16
|
14 #endif
|
Chris@16
|
15
|
Chris@16
|
16 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
|
Chris@16
|
17 #include <boost/detail/allocator_utilities.hpp>
|
Chris@101
|
18 #include <utility>
|
Chris@16
|
19
|
Chris@16
|
20 namespace boost{
|
Chris@16
|
21
|
Chris@16
|
22 namespace multi_index{
|
Chris@16
|
23
|
Chris@16
|
24 namespace detail{
|
Chris@16
|
25
|
Chris@101
|
26 /* Certain C++ requirements on unordered associative containers (see LWG issue
|
Chris@101
|
27 * #579) imply a data structure where nodes are linked in a single list, which
|
Chris@101
|
28 * in its turn forces implementors to add additional overhed per node to
|
Chris@101
|
29 * associate each with its corresponding bucket. Others resort to storing hash
|
Chris@101
|
30 * values, we use an alternative structure providing unconditional O(1)
|
Chris@101
|
31 * manipulation, even in situations of unfair hash distribution, plus some
|
Chris@101
|
32 * lookup speedups. For unique indices we maintain a doubly linked list of
|
Chris@101
|
33 * nodes except that if N is the first node of a bucket its associated
|
Chris@101
|
34 * bucket node is embedded between N and the preceding node in the following
|
Chris@101
|
35 * manner:
|
Chris@101
|
36 *
|
Chris@101
|
37 * +---+ +---+ +---+ +---+
|
Chris@101
|
38 * <--+ |<--+ | <--+ |<--+ |
|
Chris@101
|
39 * ... | B0| | B1| ... | B1| | B2| ...
|
Chris@101
|
40 * | |-+ | +--> | |-+ | +-->
|
Chris@101
|
41 * +-+-+ | +---+ +-+-+ | +---+
|
Chris@101
|
42 * | ^ | ^
|
Chris@101
|
43 * | | | |
|
Chris@101
|
44 * | +-+ | +-+
|
Chris@101
|
45 * | | | |
|
Chris@101
|
46 * v | v |
|
Chris@101
|
47 * --+---+---+---+-- --+---+---+---+--
|
Chris@101
|
48 * ... | | B1| | ... | | B2| | ...
|
Chris@101
|
49 * --+---+---+---+-- --+---+---+---+--
|
Chris@101
|
50 *
|
Chris@101
|
51 *
|
Chris@101
|
52 * The fist and last nodes of buckets can be checked with
|
Chris@101
|
53 *
|
Chris@101
|
54 * first node of a bucket: Npn != N
|
Chris@101
|
55 * last node of a bucket: Nnp != N
|
Chris@101
|
56 *
|
Chris@101
|
57 * (n and p short for ->next(), ->prior(), bucket nodes have prior pointers
|
Chris@101
|
58 * only). Pure insert and erase (without lookup) can be unconditionally done
|
Chris@101
|
59 * in O(1).
|
Chris@101
|
60 * For non-unique indices we add the following additional complexity: when
|
Chris@101
|
61 * there is a group of 3 or more equivalent elements, they are linked as
|
Chris@101
|
62 * follows:
|
Chris@101
|
63 *
|
Chris@101
|
64 * +-----------------------+
|
Chris@101
|
65 * | v
|
Chris@101
|
66 * +---+ | +---+ +---+ +---+
|
Chris@101
|
67 * | | +-+ | | |<--+ |
|
Chris@101
|
68 * | F | | S | ... | P | | L |
|
Chris@101
|
69 * | +-->| | | +-+ | |
|
Chris@101
|
70 * +---+ +---+ +---+ | +---+
|
Chris@101
|
71 * ^ |
|
Chris@101
|
72 * +-----------------------+
|
Chris@101
|
73 *
|
Chris@101
|
74 * F, S, P and L are the first, second, penultimate and last node in the
|
Chris@101
|
75 * group, respectively (S and P can coincide if the group has size 3.) This
|
Chris@101
|
76 * arrangement is used to skip equivalent elements in O(1) when doing lookup,
|
Chris@101
|
77 * while preserving O(1) insert/erase. The following invariants identify
|
Chris@101
|
78 * special positions (some of the operations have to be carefully implemented
|
Chris@101
|
79 * as Xnn is not valid if Xn points to a bucket):
|
Chris@101
|
80 *
|
Chris@101
|
81 * first node of a bucket: Npnp == N
|
Chris@101
|
82 * last node of a bucket: Nnpp == N
|
Chris@101
|
83 * first node of a group: Nnp != N && Nnppn == N
|
Chris@101
|
84 * second node of a group: Npn != N && Nppnn == N
|
Chris@101
|
85 * n-1 node of a group: Nnp != N && Nnnpp == N
|
Chris@101
|
86 * last node of a group: Npn != N && Npnnp == N
|
Chris@101
|
87 *
|
Chris@101
|
88 * The memory overhead is one pointer per bucket plus two pointers per node,
|
Chris@101
|
89 * probably unbeatable. The resulting structure is bidirectonally traversable,
|
Chris@101
|
90 * though currently we are just providing forward iteration.
|
Chris@101
|
91 */
|
Chris@16
|
92
|
Chris@16
|
93 template<typename Allocator>
|
Chris@101
|
94 struct hashed_index_node_impl;
|
Chris@101
|
95
|
Chris@101
|
96 /* half-header (only prior() pointer) to use for the bucket array */
|
Chris@101
|
97
|
Chris@101
|
98 template<typename Allocator>
|
Chris@101
|
99 struct hashed_index_base_node_impl
|
Chris@16
|
100 {
|
Chris@101
|
101 typedef typename
|
Chris@101
|
102 boost::detail::allocator::rebind_to<
|
Chris@101
|
103 Allocator,hashed_index_base_node_impl
|
Chris@101
|
104 >::type::pointer base_pointer;
|
Chris@101
|
105 typedef typename
|
Chris@101
|
106 boost::detail::allocator::rebind_to<
|
Chris@101
|
107 Allocator,hashed_index_base_node_impl
|
Chris@101
|
108 >::type::const_pointer const_base_pointer;
|
Chris@101
|
109 typedef typename
|
Chris@101
|
110 boost::detail::allocator::rebind_to<
|
Chris@16
|
111 Allocator,
|
Chris@101
|
112 hashed_index_node_impl<Allocator>
|
Chris@101
|
113 >::type::pointer pointer;
|
Chris@101
|
114 typedef typename
|
Chris@101
|
115 boost::detail::allocator::rebind_to<
|
Chris@16
|
116 Allocator,
|
Chris@101
|
117 hashed_index_node_impl<Allocator>
|
Chris@101
|
118 >::type::const_pointer const_pointer;
|
Chris@16
|
119
|
Chris@101
|
120 pointer& prior(){return prior_;}
|
Chris@101
|
121 pointer prior()const{return prior_;}
|
Chris@16
|
122
|
Chris@101
|
123 private:
|
Chris@101
|
124 pointer prior_;
|
Chris@101
|
125 };
|
Chris@16
|
126
|
Chris@101
|
127 /* full header (prior() and next()) for the nodes */
|
Chris@101
|
128
|
Chris@101
|
129 template<typename Allocator>
|
Chris@101
|
130 struct hashed_index_node_impl:hashed_index_base_node_impl<Allocator>
|
Chris@101
|
131 {
|
Chris@101
|
132 private:
|
Chris@101
|
133 typedef hashed_index_base_node_impl<Allocator> super;
|
Chris@101
|
134
|
Chris@101
|
135 public:
|
Chris@101
|
136 typedef typename super::base_pointer base_pointer;
|
Chris@101
|
137 typedef typename super::const_base_pointer const_base_pointer;
|
Chris@101
|
138 typedef typename super::pointer pointer;
|
Chris@101
|
139 typedef typename super::const_pointer const_pointer;
|
Chris@101
|
140
|
Chris@101
|
141 base_pointer& next(){return next_;}
|
Chris@101
|
142 base_pointer next()const{return next_;}
|
Chris@101
|
143
|
Chris@101
|
144 static pointer pointer_from(base_pointer x)
|
Chris@16
|
145 {
|
Chris@101
|
146 return static_cast<pointer>(static_cast<hashed_index_node_impl*>(&*x));
|
Chris@101
|
147 }
|
Chris@16
|
148
|
Chris@101
|
149 static base_pointer base_pointer_from(pointer x)
|
Chris@101
|
150 {
|
Chris@101
|
151 return static_cast<base_pointer>(&*x);
|
Chris@101
|
152 }
|
Chris@101
|
153
|
Chris@101
|
154 private:
|
Chris@101
|
155 base_pointer next_;
|
Chris@101
|
156 };
|
Chris@101
|
157
|
Chris@101
|
158 /* Boost.MultiIndex requires machinery to reverse unlink operations. A simple
|
Chris@101
|
159 * way to make a pointer-manipulation function undoable is to templatize
|
Chris@101
|
160 * its internal pointer assignments with a functor that, besides doing the
|
Chris@101
|
161 * assignment, keeps track of the original pointer values and can later undo
|
Chris@101
|
162 * the operations in reverse order.
|
Chris@101
|
163 */
|
Chris@101
|
164
|
Chris@101
|
165 struct default_assigner
|
Chris@101
|
166 {
|
Chris@101
|
167 template<typename T> void operator()(T& x,const T& val){x=val;}
|
Chris@101
|
168 };
|
Chris@101
|
169
|
Chris@101
|
170 template<typename Node>
|
Chris@101
|
171 struct unlink_undo_assigner
|
Chris@101
|
172 {
|
Chris@101
|
173 typedef typename Node::base_pointer base_pointer;
|
Chris@101
|
174 typedef typename Node::pointer pointer;
|
Chris@101
|
175
|
Chris@101
|
176 unlink_undo_assigner():pointer_track_count(0),base_pointer_track_count(0){}
|
Chris@101
|
177
|
Chris@101
|
178 void operator()(pointer& x,pointer val)
|
Chris@101
|
179 {
|
Chris@101
|
180 pointer_tracks[pointer_track_count].x=&x;
|
Chris@101
|
181 pointer_tracks[pointer_track_count++].val=x;
|
Chris@101
|
182 x=val;
|
Chris@101
|
183 }
|
Chris@101
|
184
|
Chris@101
|
185 void operator()(base_pointer& x,base_pointer val)
|
Chris@101
|
186 {
|
Chris@101
|
187 base_pointer_tracks[base_pointer_track_count].x=&x;
|
Chris@101
|
188 base_pointer_tracks[base_pointer_track_count++].val=x;
|
Chris@101
|
189 x=val;
|
Chris@101
|
190 }
|
Chris@101
|
191
|
Chris@101
|
192 void operator()() /* undo op */
|
Chris@101
|
193 {
|
Chris@101
|
194 /* in the absence of aliasing, restitution order is immaterial */
|
Chris@101
|
195
|
Chris@101
|
196 while(pointer_track_count--){
|
Chris@101
|
197 *(pointer_tracks[pointer_track_count].x)=
|
Chris@101
|
198 pointer_tracks[pointer_track_count].val;
|
Chris@101
|
199 }
|
Chris@101
|
200 while(base_pointer_track_count--){
|
Chris@101
|
201 *(base_pointer_tracks[base_pointer_track_count].x)=
|
Chris@101
|
202 base_pointer_tracks[base_pointer_track_count].val;
|
Chris@16
|
203 }
|
Chris@16
|
204 }
|
Chris@16
|
205
|
Chris@101
|
206 struct pointer_track {pointer* x; pointer val;};
|
Chris@101
|
207 struct base_pointer_track{base_pointer* x; base_pointer val;};
|
Chris@101
|
208
|
Chris@101
|
209 /* We know the maximum number of pointer and base pointer assignments that
|
Chris@101
|
210 * the two unlink versions do, so we can statically reserve the needed
|
Chris@101
|
211 * storage.
|
Chris@101
|
212 */
|
Chris@101
|
213
|
Chris@101
|
214 pointer_track pointer_tracks[3];
|
Chris@101
|
215 int pointer_track_count;
|
Chris@101
|
216 base_pointer_track base_pointer_tracks[2];
|
Chris@101
|
217 int base_pointer_track_count;
|
Chris@101
|
218 };
|
Chris@101
|
219
|
Chris@101
|
220 /* algorithmic stuff for unique and non-unique variants */
|
Chris@101
|
221
|
Chris@101
|
222 struct hashed_unique_tag{};
|
Chris@101
|
223 struct hashed_non_unique_tag{};
|
Chris@101
|
224
|
Chris@101
|
225 template<typename Node,typename Category>
|
Chris@101
|
226 struct hashed_index_node_alg;
|
Chris@101
|
227
|
Chris@101
|
228 template<typename Node>
|
Chris@101
|
229 struct hashed_index_node_alg<Node,hashed_unique_tag>
|
Chris@101
|
230 {
|
Chris@101
|
231 typedef typename Node::base_pointer base_pointer;
|
Chris@101
|
232 typedef typename Node::const_base_pointer const_base_pointer;
|
Chris@101
|
233 typedef typename Node::pointer pointer;
|
Chris@101
|
234 typedef typename Node::const_pointer const_pointer;
|
Chris@101
|
235
|
Chris@101
|
236 static bool is_first_of_bucket(pointer x)
|
Chris@16
|
237 {
|
Chris@101
|
238 return x->prior()->next()!=base_pointer_from(x);
|
Chris@101
|
239 }
|
Chris@101
|
240
|
Chris@101
|
241 static pointer after(pointer x)
|
Chris@101
|
242 {
|
Chris@101
|
243 return is_last_of_bucket(x)?x->next()->prior():pointer_from(x->next());
|
Chris@101
|
244 }
|
Chris@101
|
245
|
Chris@101
|
246 static pointer after_local(pointer x)
|
Chris@101
|
247 {
|
Chris@101
|
248 return is_last_of_bucket(x)?pointer(0):pointer_from(x->next());
|
Chris@101
|
249 }
|
Chris@101
|
250
|
Chris@101
|
251 static pointer next_to_inspect(pointer x)
|
Chris@101
|
252 {
|
Chris@101
|
253 return is_last_of_bucket(x)?pointer(0):pointer_from(x->next());
|
Chris@101
|
254 }
|
Chris@101
|
255
|
Chris@101
|
256 static void link(pointer x,base_pointer buc,pointer end)
|
Chris@101
|
257 {
|
Chris@101
|
258 if(buc->prior()==pointer(0)){ /* empty bucket */
|
Chris@101
|
259 x->prior()=end->prior();
|
Chris@101
|
260 x->next()=end->prior()->next();
|
Chris@101
|
261 x->prior()->next()=buc;
|
Chris@101
|
262 buc->prior()=x;
|
Chris@101
|
263 end->prior()=x;
|
Chris@101
|
264 }
|
Chris@101
|
265 else{
|
Chris@101
|
266 x->prior()=buc->prior()->prior();
|
Chris@101
|
267 x->next()=base_pointer_from(buc->prior());
|
Chris@101
|
268 buc->prior()=x;
|
Chris@101
|
269 x->next()->prior()=x;
|
Chris@101
|
270 }
|
Chris@101
|
271 }
|
Chris@16
|
272
|
Chris@16
|
273 static void unlink(pointer x)
|
Chris@16
|
274 {
|
Chris@101
|
275 default_assigner assign;
|
Chris@101
|
276 unlink(x,assign);
|
Chris@16
|
277 }
|
Chris@16
|
278
|
Chris@101
|
279 typedef unlink_undo_assigner<Node> unlink_undo;
|
Chris@101
|
280
|
Chris@101
|
281 template<typename Assigner>
|
Chris@101
|
282 static void unlink(pointer x,Assigner& assign)
|
Chris@16
|
283 {
|
Chris@101
|
284 if(is_first_of_bucket(x)){
|
Chris@101
|
285 if(is_last_of_bucket(x)){
|
Chris@101
|
286 assign(x->prior()->next()->prior(),pointer(0));
|
Chris@101
|
287 assign(x->prior()->next(),x->next());
|
Chris@101
|
288 assign(x->next()->prior()->prior(),x->prior());
|
Chris@101
|
289 }
|
Chris@101
|
290 else{
|
Chris@101
|
291 assign(x->prior()->next()->prior(),pointer_from(x->next()));
|
Chris@101
|
292 assign(x->next()->prior(),x->prior());
|
Chris@101
|
293 }
|
Chris@101
|
294 }
|
Chris@101
|
295 else if(is_last_of_bucket(x)){
|
Chris@101
|
296 assign(x->prior()->next(),x->next());
|
Chris@101
|
297 assign(x->next()->prior()->prior(),x->prior());
|
Chris@101
|
298 }
|
Chris@101
|
299 else{
|
Chris@101
|
300 assign(x->prior()->next(),x->next());
|
Chris@101
|
301 assign(x->next()->prior(),x->prior());
|
Chris@101
|
302 }
|
Chris@16
|
303 }
|
Chris@16
|
304
|
Chris@101
|
305 /* used only at rehashing */
|
Chris@101
|
306
|
Chris@101
|
307 static void append(pointer x,pointer end)
|
Chris@16
|
308 {
|
Chris@101
|
309 x->prior()=end->prior();
|
Chris@101
|
310 x->next()=end->prior()->next();
|
Chris@101
|
311 x->prior()->next()=base_pointer_from(x);
|
Chris@101
|
312 end->prior()=x;
|
Chris@101
|
313 }
|
Chris@101
|
314
|
Chris@101
|
315 static bool unlink_last(pointer end)
|
Chris@101
|
316 {
|
Chris@101
|
317 /* returns true iff bucket is emptied */
|
Chris@101
|
318
|
Chris@101
|
319 pointer x=end->prior();
|
Chris@101
|
320 if(x->prior()->next()==base_pointer_from(x)){
|
Chris@101
|
321 x->prior()->next()=x->next();
|
Chris@101
|
322 end->prior()=x->prior();
|
Chris@101
|
323 return false;
|
Chris@101
|
324 }
|
Chris@101
|
325 else{
|
Chris@101
|
326 x->prior()->next()->prior()=pointer(0);
|
Chris@101
|
327 x->prior()->next()=x->next();
|
Chris@101
|
328 end->prior()=x->prior();
|
Chris@101
|
329 return true;
|
Chris@101
|
330 }
|
Chris@16
|
331 }
|
Chris@16
|
332
|
Chris@16
|
333 private:
|
Chris@101
|
334 static pointer pointer_from(base_pointer x)
|
Chris@101
|
335 {
|
Chris@101
|
336 return Node::pointer_from(x);
|
Chris@101
|
337 }
|
Chris@101
|
338
|
Chris@101
|
339 static base_pointer base_pointer_from(pointer x)
|
Chris@101
|
340 {
|
Chris@101
|
341 return Node::base_pointer_from(x);
|
Chris@101
|
342 }
|
Chris@101
|
343
|
Chris@101
|
344 static bool is_last_of_bucket(pointer x)
|
Chris@101
|
345 {
|
Chris@101
|
346 return x->next()->prior()!=x;
|
Chris@101
|
347 }
|
Chris@101
|
348 };
|
Chris@101
|
349
|
Chris@101
|
350 template<typename Node>
|
Chris@101
|
351 struct hashed_index_node_alg<Node,hashed_non_unique_tag>
|
Chris@101
|
352 {
|
Chris@101
|
353 typedef typename Node::base_pointer base_pointer;
|
Chris@101
|
354 typedef typename Node::const_base_pointer const_base_pointer;
|
Chris@101
|
355 typedef typename Node::pointer pointer;
|
Chris@101
|
356 typedef typename Node::const_pointer const_pointer;
|
Chris@101
|
357
|
Chris@101
|
358 static bool is_first_of_bucket(pointer x)
|
Chris@101
|
359 {
|
Chris@101
|
360 return x->prior()->next()->prior()==x;
|
Chris@101
|
361 }
|
Chris@101
|
362
|
Chris@101
|
363 static bool is_first_of_group(pointer x)
|
Chris@101
|
364 {
|
Chris@101
|
365 return
|
Chris@101
|
366 x->next()->prior()!=x&&
|
Chris@101
|
367 x->next()->prior()->prior()->next()==base_pointer_from(x);
|
Chris@101
|
368 }
|
Chris@101
|
369
|
Chris@101
|
370 static pointer after(pointer x)
|
Chris@101
|
371 {
|
Chris@101
|
372 if(x->next()->prior()==x)return pointer_from(x->next());
|
Chris@101
|
373 if(x->next()->prior()->prior()==x)return x->next()->prior();
|
Chris@101
|
374 if(x->next()->prior()->prior()->next()==base_pointer_from(x))
|
Chris@101
|
375 return pointer_from(x->next());
|
Chris@101
|
376 return pointer_from(x->next())->next()->prior();
|
Chris@101
|
377 }
|
Chris@101
|
378
|
Chris@101
|
379 static pointer after_local(pointer x)
|
Chris@101
|
380 {
|
Chris@101
|
381 if(x->next()->prior()==x)return pointer_from(x->next());
|
Chris@101
|
382 if(x->next()->prior()->prior()==x)return pointer(0);
|
Chris@101
|
383 if(x->next()->prior()->prior()->next()==base_pointer_from(x))
|
Chris@101
|
384 return pointer_from(x->next());
|
Chris@101
|
385 return pointer_from(x->next())->next()->prior();
|
Chris@101
|
386 }
|
Chris@101
|
387
|
Chris@101
|
388 static pointer next_to_inspect(pointer x)
|
Chris@101
|
389 {
|
Chris@101
|
390 if(x->next()->prior()==x)return pointer_from(x->next());
|
Chris@101
|
391 if(x->next()->prior()->prior()==x)return pointer(0);
|
Chris@101
|
392 if(x->next()->prior()->next()->prior()!=x->next()->prior())
|
Chris@101
|
393 return pointer(0);
|
Chris@101
|
394 return pointer_from(x->next()->prior()->next());
|
Chris@101
|
395 }
|
Chris@101
|
396
|
Chris@101
|
397 static void link(pointer x,base_pointer buc,pointer end)
|
Chris@101
|
398 {
|
Chris@101
|
399 if(buc->prior()==pointer(0)){ /* empty bucket */
|
Chris@101
|
400 x->prior()=end->prior();
|
Chris@101
|
401 x->next()=end->prior()->next();
|
Chris@101
|
402 x->prior()->next()=buc;
|
Chris@101
|
403 buc->prior()=x;
|
Chris@101
|
404 end->prior()=x;
|
Chris@101
|
405 }
|
Chris@101
|
406 else{
|
Chris@101
|
407 x->prior()=buc->prior()->prior();
|
Chris@101
|
408 x->next()=base_pointer_from(buc->prior());
|
Chris@101
|
409 buc->prior()=x;
|
Chris@101
|
410 x->next()->prior()=x;
|
Chris@101
|
411 }
|
Chris@101
|
412 };
|
Chris@101
|
413
|
Chris@101
|
414 static void link(pointer x,pointer first,pointer last)
|
Chris@101
|
415 {
|
Chris@101
|
416 x->prior()=first->prior();
|
Chris@101
|
417 x->next()=base_pointer_from(first);
|
Chris@101
|
418 if(is_first_of_bucket(first)){
|
Chris@101
|
419 x->prior()->next()->prior()=x;
|
Chris@101
|
420 }
|
Chris@101
|
421 else{
|
Chris@101
|
422 x->prior()->next()=base_pointer_from(x);
|
Chris@101
|
423 }
|
Chris@101
|
424
|
Chris@101
|
425 if(first==last){
|
Chris@101
|
426 last->prior()=x;
|
Chris@101
|
427 }
|
Chris@101
|
428 else if(first->next()==base_pointer_from(last)){
|
Chris@101
|
429 first->prior()=last;
|
Chris@101
|
430 first->next()=base_pointer_from(x);
|
Chris@101
|
431 }
|
Chris@101
|
432 else{
|
Chris@101
|
433 pointer second=pointer_from(first->next()),
|
Chris@101
|
434 lastbutone=last->prior();
|
Chris@101
|
435 second->prior()=first;
|
Chris@101
|
436 first->prior()=last;
|
Chris@101
|
437 lastbutone->next()=base_pointer_from(x);
|
Chris@101
|
438 }
|
Chris@101
|
439 }
|
Chris@101
|
440
|
Chris@101
|
441 static void unlink(pointer x)
|
Chris@101
|
442 {
|
Chris@101
|
443 default_assigner assign;
|
Chris@101
|
444 unlink(x,assign);
|
Chris@101
|
445 }
|
Chris@101
|
446
|
Chris@101
|
447 typedef unlink_undo_assigner<Node> unlink_undo;
|
Chris@101
|
448
|
Chris@101
|
449 template<typename Assigner>
|
Chris@101
|
450 static void unlink(pointer x,Assigner& assign)
|
Chris@101
|
451 {
|
Chris@101
|
452 if(x->prior()->next()==base_pointer_from(x)){
|
Chris@101
|
453 if(x->next()->prior()==x){
|
Chris@101
|
454 left_unlink(x,assign);
|
Chris@101
|
455 right_unlink(x,assign);
|
Chris@101
|
456 }
|
Chris@101
|
457 else if(x->next()->prior()->prior()==x){ /* last of bucket */
|
Chris@101
|
458 left_unlink(x,assign);
|
Chris@101
|
459 right_unlink_last_of_bucket(x,assign);
|
Chris@101
|
460 }
|
Chris@101
|
461 else if(x->next()->prior()->prior()->next()==
|
Chris@101
|
462 base_pointer_from(x)){ /* first of group size */
|
Chris@101
|
463 left_unlink(x,assign);
|
Chris@101
|
464 right_unlink_first_of_group(x,assign);
|
Chris@101
|
465 }
|
Chris@101
|
466 else{ /* n-1 of group */
|
Chris@101
|
467 unlink_last_but_one_of_group(x,assign);
|
Chris@101
|
468 }
|
Chris@101
|
469 }
|
Chris@101
|
470 else if(x->prior()->next()->prior()==x){ /* first of bucket */
|
Chris@101
|
471 if(x->next()->prior()==x){
|
Chris@101
|
472 left_unlink_first_of_bucket(x,assign);
|
Chris@101
|
473 right_unlink(x,assign);
|
Chris@101
|
474 }
|
Chris@101
|
475 else if(x->next()->prior()->prior()==x){ /* last of bucket */
|
Chris@101
|
476 assign(x->prior()->next()->prior(),pointer(0));
|
Chris@101
|
477 assign(x->prior()->next(),x->next());
|
Chris@101
|
478 assign(x->next()->prior()->prior(),x->prior());
|
Chris@101
|
479 }
|
Chris@101
|
480 else{ /* first of group */
|
Chris@101
|
481 left_unlink_first_of_bucket(x,assign);
|
Chris@101
|
482 right_unlink_first_of_group(x,assign);
|
Chris@101
|
483 }
|
Chris@101
|
484 }
|
Chris@101
|
485 else if(x->next()->prior()->prior()==x){ /* last of group and bucket */
|
Chris@101
|
486 left_unlink_last_of_group(x,assign);
|
Chris@101
|
487 right_unlink_last_of_bucket(x,assign);
|
Chris@101
|
488 }
|
Chris@101
|
489 else if(pointer_from(x->prior()->prior()->next())
|
Chris@101
|
490 ->next()==base_pointer_from(x)){ /* second of group */
|
Chris@101
|
491 unlink_second_of_group(x,assign);
|
Chris@101
|
492 }
|
Chris@101
|
493 else{ /* last of group, ~(last of bucket) */
|
Chris@101
|
494 left_unlink_last_of_group(x,assign);
|
Chris@101
|
495 right_unlink(x,assign);
|
Chris@101
|
496 }
|
Chris@101
|
497 }
|
Chris@101
|
498
|
Chris@101
|
499 /* used only at rehashing */
|
Chris@101
|
500
|
Chris@101
|
501 static void link_range(
|
Chris@101
|
502 pointer first,pointer last,base_pointer buc,pointer cend)
|
Chris@101
|
503 {
|
Chris@101
|
504 if(buc->prior()==pointer(0)){ /* empty bucket */
|
Chris@101
|
505 first->prior()=cend->prior();
|
Chris@101
|
506 last->next()=cend->prior()->next();
|
Chris@101
|
507 first->prior()->next()=buc;
|
Chris@101
|
508 buc->prior()=first;
|
Chris@101
|
509 cend->prior()=last;
|
Chris@101
|
510 }
|
Chris@101
|
511 else{
|
Chris@101
|
512 first->prior()=buc->prior()->prior();
|
Chris@101
|
513 last->next()=base_pointer_from(buc->prior());
|
Chris@101
|
514 buc->prior()=first;
|
Chris@101
|
515 last->next()->prior()=last;
|
Chris@101
|
516 }
|
Chris@101
|
517 }
|
Chris@101
|
518
|
Chris@101
|
519 static void append_range(pointer first,pointer last,pointer cend)
|
Chris@101
|
520 {
|
Chris@101
|
521 first->prior()=cend->prior();
|
Chris@101
|
522 last->next()=cend->prior()->next();
|
Chris@101
|
523 first->prior()->next()=base_pointer_from(first);
|
Chris@101
|
524 cend->prior()=last;
|
Chris@101
|
525 }
|
Chris@101
|
526
|
Chris@101
|
527 static std::pair<pointer,bool> unlink_last_group(pointer end)
|
Chris@101
|
528 {
|
Chris@101
|
529 /* returns first of group true iff bucket is emptied */
|
Chris@101
|
530
|
Chris@101
|
531 pointer x=end->prior();
|
Chris@101
|
532 if(x->prior()->next()==base_pointer_from(x)){
|
Chris@101
|
533 x->prior()->next()=x->next();
|
Chris@101
|
534 end->prior()=x->prior();
|
Chris@101
|
535 return std::make_pair(x,false);
|
Chris@101
|
536 }
|
Chris@101
|
537 else if(x->prior()->next()->prior()==x){
|
Chris@101
|
538 x->prior()->next()->prior()=pointer(0);
|
Chris@101
|
539 x->prior()->next()=x->next();
|
Chris@101
|
540 end->prior()=x->prior();
|
Chris@101
|
541 return std::make_pair(x,true);
|
Chris@101
|
542 }
|
Chris@101
|
543 else{
|
Chris@101
|
544 pointer y=pointer_from(x->prior()->next());
|
Chris@101
|
545
|
Chris@101
|
546 if(y->prior()->next()==base_pointer_from(y)){
|
Chris@101
|
547 y->prior()->next()=x->next();
|
Chris@101
|
548 end->prior()=y->prior();
|
Chris@101
|
549 return std::make_pair(y,false);
|
Chris@101
|
550 }
|
Chris@101
|
551 else{
|
Chris@101
|
552 y->prior()->next()->prior()=pointer(0);
|
Chris@101
|
553 y->prior()->next()=x->next();
|
Chris@101
|
554 end->prior()=y->prior();
|
Chris@101
|
555 return std::make_pair(y,true);
|
Chris@101
|
556 }
|
Chris@101
|
557 }
|
Chris@101
|
558 }
|
Chris@101
|
559
|
Chris@101
|
560 static void unlink_range(pointer first,pointer last)
|
Chris@101
|
561 {
|
Chris@101
|
562 if(is_first_of_bucket(first)){
|
Chris@101
|
563 if(is_last_of_bucket(last)){
|
Chris@101
|
564 first->prior()->next()->prior()=pointer(0);
|
Chris@101
|
565 first->prior()->next()=last->next();
|
Chris@101
|
566 last->next()->prior()->prior()=first->prior();
|
Chris@101
|
567 }
|
Chris@101
|
568 else{
|
Chris@101
|
569 first->prior()->next()->prior()=pointer_from(last->next());
|
Chris@101
|
570 last->next()->prior()=first->prior();
|
Chris@101
|
571 }
|
Chris@101
|
572 }
|
Chris@101
|
573 else if(is_last_of_bucket(last)){
|
Chris@101
|
574 first->prior()->next()=last->next();
|
Chris@101
|
575 last->next()->prior()->prior()=first->prior();
|
Chris@101
|
576 }
|
Chris@101
|
577 else{
|
Chris@101
|
578 first->prior()->next()=last->next();
|
Chris@101
|
579 last->next()->prior()=first->prior();
|
Chris@101
|
580 }
|
Chris@101
|
581 }
|
Chris@101
|
582
|
Chris@101
|
583 private:
|
Chris@101
|
584 static pointer pointer_from(base_pointer x)
|
Chris@101
|
585 {
|
Chris@101
|
586 return Node::pointer_from(x);
|
Chris@101
|
587 }
|
Chris@101
|
588
|
Chris@101
|
589 static base_pointer base_pointer_from(pointer x)
|
Chris@101
|
590 {
|
Chris@101
|
591 return Node::base_pointer_from(x);
|
Chris@101
|
592 }
|
Chris@101
|
593
|
Chris@101
|
594 static bool is_last_of_bucket(pointer x)
|
Chris@101
|
595 {
|
Chris@101
|
596 return x->next()->prior()->prior()==x;
|
Chris@101
|
597 }
|
Chris@101
|
598
|
Chris@101
|
599 template<typename Assigner>
|
Chris@101
|
600 static void left_unlink(pointer x,Assigner& assign)
|
Chris@101
|
601 {
|
Chris@101
|
602 assign(x->prior()->next(),x->next());
|
Chris@101
|
603 }
|
Chris@101
|
604
|
Chris@101
|
605 template<typename Assigner>
|
Chris@101
|
606 static void right_unlink(pointer x,Assigner& assign)
|
Chris@101
|
607 {
|
Chris@101
|
608 assign(x->next()->prior(),x->prior());
|
Chris@101
|
609 }
|
Chris@101
|
610
|
Chris@101
|
611 template<typename Assigner>
|
Chris@101
|
612 static void left_unlink_first_of_bucket(pointer x,Assigner& assign)
|
Chris@101
|
613 {
|
Chris@101
|
614 assign(x->prior()->next()->prior(),pointer_from(x->next()));
|
Chris@101
|
615 }
|
Chris@101
|
616
|
Chris@101
|
617 template<typename Assigner>
|
Chris@101
|
618 static void right_unlink_last_of_bucket(pointer x,Assigner& assign)
|
Chris@101
|
619 {
|
Chris@101
|
620 assign(x->next()->prior()->prior(),x->prior());
|
Chris@101
|
621 }
|
Chris@101
|
622
|
Chris@101
|
623 template<typename Assigner>
|
Chris@101
|
624 static void right_unlink_first_of_group(pointer x,Assigner& assign)
|
Chris@101
|
625 {
|
Chris@101
|
626 pointer second=pointer_from(x->next()),
|
Chris@101
|
627 last=second->prior(),
|
Chris@101
|
628 lastbutone=last->prior();
|
Chris@101
|
629 if(second==lastbutone){
|
Chris@101
|
630 assign(second->next(),base_pointer_from(last));
|
Chris@101
|
631 assign(second->prior(),x->prior());
|
Chris@101
|
632 }
|
Chris@101
|
633 else{
|
Chris@101
|
634 assign(lastbutone->next(),base_pointer_from(second));
|
Chris@101
|
635 assign(second->next()->prior(),last);
|
Chris@101
|
636 assign(second->prior(),x->prior());
|
Chris@101
|
637 }
|
Chris@101
|
638 }
|
Chris@101
|
639
|
Chris@101
|
640 template<typename Assigner>
|
Chris@101
|
641 static void left_unlink_last_of_group(pointer x,Assigner& assign)
|
Chris@101
|
642 {
|
Chris@101
|
643 pointer lastbutone=x->prior(),
|
Chris@101
|
644 first=pointer_from(lastbutone->next()),
|
Chris@101
|
645 second=pointer_from(first->next());
|
Chris@101
|
646 if(lastbutone==second){
|
Chris@101
|
647 assign(lastbutone->prior(),first);
|
Chris@101
|
648 assign(lastbutone->next(),x->next());
|
Chris@101
|
649 }
|
Chris@101
|
650 else{
|
Chris@101
|
651 assign(second->prior(),lastbutone);
|
Chris@101
|
652 assign(lastbutone->prior()->next(),base_pointer_from(first));
|
Chris@101
|
653 assign(lastbutone->next(),x->next());
|
Chris@101
|
654 }
|
Chris@101
|
655 }
|
Chris@101
|
656
|
Chris@101
|
657 template<typename Assigner>
|
Chris@101
|
658 static void unlink_last_but_one_of_group(pointer x,Assigner& assign)
|
Chris@101
|
659 {
|
Chris@101
|
660 pointer first=pointer_from(x->next()),
|
Chris@101
|
661 second=pointer_from(first->next()),
|
Chris@101
|
662 last=second->prior();
|
Chris@101
|
663 if(second==x){
|
Chris@101
|
664 assign(last->prior(),first);
|
Chris@101
|
665 assign(first->next(),base_pointer_from(last));
|
Chris@101
|
666 }
|
Chris@101
|
667 else{
|
Chris@101
|
668 assign(last->prior(),x->prior());
|
Chris@101
|
669 assign(x->prior()->next(),base_pointer_from(first));
|
Chris@101
|
670 }
|
Chris@101
|
671 }
|
Chris@101
|
672
|
Chris@101
|
673 template<typename Assigner>
|
Chris@101
|
674 static void unlink_second_of_group(pointer x,Assigner& assign)
|
Chris@101
|
675 {
|
Chris@101
|
676 pointer last=x->prior(),
|
Chris@101
|
677 lastbutone=last->prior(),
|
Chris@101
|
678 first=pointer_from(lastbutone->next());
|
Chris@101
|
679 if(lastbutone==x){
|
Chris@101
|
680 assign(first->next(),base_pointer_from(last));
|
Chris@101
|
681 assign(last->prior(),first);
|
Chris@101
|
682 }
|
Chris@101
|
683 else{
|
Chris@101
|
684 assign(first->next(),x->next());
|
Chris@101
|
685 assign(x->next()->prior(),last);
|
Chris@101
|
686 }
|
Chris@101
|
687 }
|
Chris@16
|
688 };
|
Chris@16
|
689
|
Chris@16
|
690 template<typename Super>
|
Chris@16
|
691 struct hashed_index_node_trampoline:
|
Chris@101
|
692 hashed_index_node_impl<
|
Chris@101
|
693 typename boost::detail::allocator::rebind_to<
|
Chris@101
|
694 typename Super::allocator_type,
|
Chris@101
|
695 char
|
Chris@101
|
696 >::type
|
Chris@101
|
697 >
|
Chris@16
|
698 {
|
Chris@101
|
699 typedef typename boost::detail::allocator::rebind_to<
|
Chris@101
|
700 typename Super::allocator_type,
|
Chris@101
|
701 char
|
Chris@101
|
702 >::type impl_allocator_type;
|
Chris@101
|
703 typedef hashed_index_node_impl<impl_allocator_type> impl_type;
|
Chris@16
|
704 };
|
Chris@16
|
705
|
Chris@101
|
706 template<typename Super,typename Category>
|
Chris@101
|
707 struct hashed_index_node:
|
Chris@101
|
708 Super,hashed_index_node_trampoline<Super>
|
Chris@16
|
709 {
|
Chris@16
|
710 private:
|
Chris@16
|
711 typedef hashed_index_node_trampoline<Super> trampoline;
|
Chris@16
|
712
|
Chris@16
|
713 public:
|
Chris@101
|
714 typedef typename trampoline::impl_type impl_type;
|
Chris@101
|
715 typedef hashed_index_node_alg<
|
Chris@101
|
716 impl_type,Category> node_alg;
|
Chris@101
|
717 typedef typename trampoline::base_pointer impl_base_pointer;
|
Chris@101
|
718 typedef typename trampoline::const_base_pointer const_impl_base_pointer;
|
Chris@101
|
719 typedef typename trampoline::pointer impl_pointer;
|
Chris@101
|
720 typedef typename trampoline::const_pointer const_impl_pointer;
|
Chris@101
|
721
|
Chris@101
|
722 impl_pointer& prior(){return trampoline::prior();}
|
Chris@101
|
723 impl_pointer prior()const{return trampoline::prior();}
|
Chris@101
|
724 impl_base_pointer& next(){return trampoline::next();}
|
Chris@101
|
725 impl_base_pointer next()const{return trampoline::next();}
|
Chris@16
|
726
|
Chris@16
|
727 impl_pointer impl()
|
Chris@16
|
728 {
|
Chris@16
|
729 return static_cast<impl_pointer>(
|
Chris@16
|
730 static_cast<impl_type*>(static_cast<trampoline*>(this)));
|
Chris@16
|
731 }
|
Chris@16
|
732
|
Chris@16
|
733 const_impl_pointer impl()const
|
Chris@16
|
734 {
|
Chris@16
|
735 return static_cast<const_impl_pointer>(
|
Chris@16
|
736 static_cast<const impl_type*>(static_cast<const trampoline*>(this)));
|
Chris@16
|
737 }
|
Chris@16
|
738
|
Chris@16
|
739 static hashed_index_node* from_impl(impl_pointer x)
|
Chris@16
|
740 {
|
Chris@16
|
741 return static_cast<hashed_index_node*>(
|
Chris@16
|
742 static_cast<trampoline*>(&*x));
|
Chris@16
|
743 }
|
Chris@16
|
744
|
Chris@16
|
745 static const hashed_index_node* from_impl(const_impl_pointer x)
|
Chris@16
|
746 {
|
Chris@16
|
747 return static_cast<const hashed_index_node*>(
|
Chris@16
|
748 static_cast<const trampoline*>(&*x));
|
Chris@16
|
749 }
|
Chris@16
|
750
|
Chris@101
|
751 /* interoperability with hashed_index_iterator */
|
Chris@101
|
752
|
Chris@101
|
753 static void increment(hashed_index_node*& x)
|
Chris@16
|
754 {
|
Chris@101
|
755 x=from_impl(node_alg::after(x->impl()));
|
Chris@101
|
756 }
|
Chris@101
|
757
|
Chris@101
|
758 static void increment_local(hashed_index_node*& x)
|
Chris@101
|
759 {
|
Chris@101
|
760 x=from_impl(node_alg::after_local(x->impl()));
|
Chris@16
|
761 }
|
Chris@16
|
762 };
|
Chris@16
|
763
|
Chris@16
|
764 } /* namespace multi_index::detail */
|
Chris@16
|
765
|
Chris@16
|
766 } /* namespace multi_index */
|
Chris@16
|
767
|
Chris@16
|
768 } /* namespace boost */
|
Chris@16
|
769
|
Chris@16
|
770 #endif
|