| /* Copyright 2003-2014 Joaquin M Lopez Munoz. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or copy at |
| * http://www.boost.org/LICENSE_1_0.txt) |
| * |
| * See http://www.boost.org/libs/multi_index for library home page. |
| */ |
| |
| #ifndef BOOST_MULTI_INDEX_DETAIL_HASH_INDEX_NODE_HPP |
| #define BOOST_MULTI_INDEX_DETAIL_HASH_INDEX_NODE_HPP |
| |
| #if defined(_MSC_VER) |
| #pragma once |
| #endif |
| |
| #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ |
| #include <boost/detail/allocator_utilities.hpp> |
| #include <utility> |
| |
| namespace boost{ |
| |
| namespace multi_index{ |
| |
| namespace detail{ |
| |
| /* Certain C++ requirements on unordered associative containers (see LWG issue |
| * #579) imply a data structure where nodes are linked in a single list, which |
| * in its turn forces implementors to add additional overhed per node to |
| * associate each with its corresponding bucket. Others resort to storing hash |
| * values, we use an alternative structure providing unconditional O(1) |
| * manipulation, even in situations of unfair hash distribution, plus some |
| * lookup speedups. For unique indices we maintain a doubly linked list of |
| * nodes except that if N is the first node of a bucket its associated |
| * bucket node is embedded between N and the preceding node in the following |
| * manner: |
| * |
| * +---+ +---+ +---+ +---+ |
| * <--+ |<--+ | <--+ |<--+ | |
| * ... | B0| | B1| ... | B1| | B2| ... |
| * | |-+ | +--> | |-+ | +--> |
| * +-+-+ | +---+ +-+-+ | +---+ |
| * | ^ | ^ |
| * | | | | |
| * | +-+ | +-+ |
| * | | | | |
| * v | v | |
| * --+---+---+---+-- --+---+---+---+-- |
| * ... | | B1| | ... | | B2| | ... |
| * --+---+---+---+-- --+---+---+---+-- |
| * |
| * |
| * The fist and last nodes of buckets can be checked with |
| * |
| * first node of a bucket: Npn != N |
| * last node of a bucket: Nnp != N |
| * |
| * (n and p short for ->next(), ->prior(), bucket nodes have prior pointers |
| * only). Pure insert and erase (without lookup) can be unconditionally done |
| * in O(1). |
| * For non-unique indices we add the following additional complexity: when |
| * there is a group of 3 or more equivalent elements, they are linked as |
| * follows: |
| * |
| * +-----------------------+ |
| * | v |
| * +---+ | +---+ +---+ +---+ |
| * | | +-+ | | |<--+ | |
| * | F | | S | ... | P | | L | |
| * | +-->| | | +-+ | | |
| * +---+ +---+ +---+ | +---+ |
| * ^ | |
| * +-----------------------+ |
| * |
| * F, S, P and L are the first, second, penultimate and last node in the |
| * group, respectively (S and P can coincide if the group has size 3.) This |
| * arrangement is used to skip equivalent elements in O(1) when doing lookup, |
| * while preserving O(1) insert/erase. The following invariants identify |
| * special positions (some of the operations have to be carefully implemented |
| * as Xnn is not valid if Xn points to a bucket): |
| * |
| * first node of a bucket: Npnp == N |
| * last node of a bucket: Nnpp == N |
| * first node of a group: Nnp != N && Nnppn == N |
| * second node of a group: Npn != N && Nppnn == N |
| * n-1 node of a group: Nnp != N && Nnnpp == N |
| * last node of a group: Npn != N && Npnnp == N |
| * |
| * The memory overhead is one pointer per bucket plus two pointers per node, |
| * probably unbeatable. The resulting structure is bidirectonally traversable, |
| * though currently we are just providing forward iteration. |
| */ |
| |
| template<typename Allocator> |
| struct hashed_index_node_impl; |
| |
| /* half-header (only prior() pointer) to use for the bucket array */ |
| |
| template<typename Allocator> |
| struct hashed_index_base_node_impl |
| { |
| typedef typename |
| boost::detail::allocator::rebind_to< |
| Allocator,hashed_index_base_node_impl |
| >::type::pointer base_pointer; |
| typedef typename |
| boost::detail::allocator::rebind_to< |
| Allocator,hashed_index_base_node_impl |
| >::type::const_pointer const_base_pointer; |
| typedef typename |
| boost::detail::allocator::rebind_to< |
| Allocator, |
| hashed_index_node_impl<Allocator> |
| >::type::pointer pointer; |
| typedef typename |
| boost::detail::allocator::rebind_to< |
| Allocator, |
| hashed_index_node_impl<Allocator> |
| >::type::const_pointer const_pointer; |
| |
| pointer& prior(){return prior_;} |
| pointer prior()const{return prior_;} |
| |
| private: |
| pointer prior_; |
| }; |
| |
| /* full header (prior() and next()) for the nodes */ |
| |
| template<typename Allocator> |
| struct hashed_index_node_impl:hashed_index_base_node_impl<Allocator> |
| { |
| private: |
| typedef hashed_index_base_node_impl<Allocator> super; |
| |
| public: |
| typedef typename super::base_pointer base_pointer; |
| typedef typename super::const_base_pointer const_base_pointer; |
| typedef typename super::pointer pointer; |
| typedef typename super::const_pointer const_pointer; |
| |
| base_pointer& next(){return next_;} |
| base_pointer next()const{return next_;} |
| |
| static pointer pointer_from(base_pointer x) |
| { |
| return static_cast<pointer>(static_cast<hashed_index_node_impl*>(&*x)); |
| } |
| |
| static base_pointer base_pointer_from(pointer x) |
| { |
| return static_cast<base_pointer>(&*x); |
| } |
| |
| private: |
| base_pointer next_; |
| }; |
| |
| /* Boost.MultiIndex requires machinery to reverse unlink operations. A simple |
| * way to make a pointer-manipulation function undoable is to templatize |
| * its internal pointer assignments with a functor that, besides doing the |
| * assignment, keeps track of the original pointer values and can later undo |
| * the operations in reverse order. |
| */ |
| |
| struct default_assigner |
| { |
| template<typename T> void operator()(T& x,const T& val){x=val;} |
| }; |
| |
| template<typename Node> |
| struct unlink_undo_assigner |
| { |
| typedef typename Node::base_pointer base_pointer; |
| typedef typename Node::pointer pointer; |
| |
| unlink_undo_assigner():pointer_track_count(0),base_pointer_track_count(0){} |
| |
| void operator()(pointer& x,pointer val) |
| { |
| pointer_tracks[pointer_track_count].x=&x; |
| pointer_tracks[pointer_track_count++].val=x; |
| x=val; |
| } |
| |
| void operator()(base_pointer& x,base_pointer val) |
| { |
| base_pointer_tracks[base_pointer_track_count].x=&x; |
| base_pointer_tracks[base_pointer_track_count++].val=x; |
| x=val; |
| } |
| |
| void operator()() /* undo op */ |
| { |
| /* in the absence of aliasing, restitution order is immaterial */ |
| |
| while(pointer_track_count--){ |
| *(pointer_tracks[pointer_track_count].x)= |
| pointer_tracks[pointer_track_count].val; |
| } |
| while(base_pointer_track_count--){ |
| *(base_pointer_tracks[base_pointer_track_count].x)= |
| base_pointer_tracks[base_pointer_track_count].val; |
| } |
| } |
| |
| struct pointer_track {pointer* x; pointer val;}; |
| struct base_pointer_track{base_pointer* x; base_pointer val;}; |
| |
| /* We know the maximum number of pointer and base pointer assignments that |
| * the two unlink versions do, so we can statically reserve the needed |
| * storage. |
| */ |
| |
| pointer_track pointer_tracks[3]; |
| int pointer_track_count; |
| base_pointer_track base_pointer_tracks[2]; |
| int base_pointer_track_count; |
| }; |
| |
| /* algorithmic stuff for unique and non-unique variants */ |
| |
| struct hashed_unique_tag{}; |
| struct hashed_non_unique_tag{}; |
| |
| template<typename Node,typename Category> |
| struct hashed_index_node_alg; |
| |
| template<typename Node> |
| struct hashed_index_node_alg<Node,hashed_unique_tag> |
| { |
| typedef typename Node::base_pointer base_pointer; |
| typedef typename Node::const_base_pointer const_base_pointer; |
| typedef typename Node::pointer pointer; |
| typedef typename Node::const_pointer const_pointer; |
| |
| static bool is_first_of_bucket(pointer x) |
| { |
| return x->prior()->next()!=base_pointer_from(x); |
| } |
| |
| static pointer after(pointer x) |
| { |
| return is_last_of_bucket(x)?x->next()->prior():pointer_from(x->next()); |
| } |
| |
| static pointer after_local(pointer x) |
| { |
| return is_last_of_bucket(x)?pointer(0):pointer_from(x->next()); |
| } |
| |
| static pointer next_to_inspect(pointer x) |
| { |
| return is_last_of_bucket(x)?pointer(0):pointer_from(x->next()); |
| } |
| |
| static void link(pointer x,base_pointer buc,pointer end) |
| { |
| if(buc->prior()==pointer(0)){ /* empty bucket */ |
| x->prior()=end->prior(); |
| x->next()=end->prior()->next(); |
| x->prior()->next()=buc; |
| buc->prior()=x; |
| end->prior()=x; |
| } |
| else{ |
| x->prior()=buc->prior()->prior(); |
| x->next()=base_pointer_from(buc->prior()); |
| buc->prior()=x; |
| x->next()->prior()=x; |
| } |
| } |
| |
| static void unlink(pointer x) |
| { |
| default_assigner assign; |
| unlink(x,assign); |
| } |
| |
| typedef unlink_undo_assigner<Node> unlink_undo; |
| |
| template<typename Assigner> |
| static void unlink(pointer x,Assigner& assign) |
| { |
| if(is_first_of_bucket(x)){ |
| if(is_last_of_bucket(x)){ |
| assign(x->prior()->next()->prior(),pointer(0)); |
| assign(x->prior()->next(),x->next()); |
| assign(x->next()->prior()->prior(),x->prior()); |
| } |
| else{ |
| assign(x->prior()->next()->prior(),pointer_from(x->next())); |
| assign(x->next()->prior(),x->prior()); |
| } |
| } |
| else if(is_last_of_bucket(x)){ |
| assign(x->prior()->next(),x->next()); |
| assign(x->next()->prior()->prior(),x->prior()); |
| } |
| else{ |
| assign(x->prior()->next(),x->next()); |
| assign(x->next()->prior(),x->prior()); |
| } |
| } |
| |
| /* used only at rehashing */ |
| |
| static void append(pointer x,pointer end) |
| { |
| x->prior()=end->prior(); |
| x->next()=end->prior()->next(); |
| x->prior()->next()=base_pointer_from(x); |
| end->prior()=x; |
| } |
| |
| static bool unlink_last(pointer end) |
| { |
| /* returns true iff bucket is emptied */ |
| |
| pointer x=end->prior(); |
| if(x->prior()->next()==base_pointer_from(x)){ |
| x->prior()->next()=x->next(); |
| end->prior()=x->prior(); |
| return false; |
| } |
| else{ |
| x->prior()->next()->prior()=pointer(0); |
| x->prior()->next()=x->next(); |
| end->prior()=x->prior(); |
| return true; |
| } |
| } |
| |
| private: |
| static pointer pointer_from(base_pointer x) |
| { |
| return Node::pointer_from(x); |
| } |
| |
| static base_pointer base_pointer_from(pointer x) |
| { |
| return Node::base_pointer_from(x); |
| } |
| |
| static bool is_last_of_bucket(pointer x) |
| { |
| return x->next()->prior()!=x; |
| } |
| }; |
| |
| template<typename Node> |
| struct hashed_index_node_alg<Node,hashed_non_unique_tag> |
| { |
| typedef typename Node::base_pointer base_pointer; |
| typedef typename Node::const_base_pointer const_base_pointer; |
| typedef typename Node::pointer pointer; |
| typedef typename Node::const_pointer const_pointer; |
| |
| static bool is_first_of_bucket(pointer x) |
| { |
| return x->prior()->next()->prior()==x; |
| } |
| |
| static bool is_first_of_group(pointer x) |
| { |
| return |
| x->next()->prior()!=x&& |
| x->next()->prior()->prior()->next()==base_pointer_from(x); |
| } |
| |
| static pointer after(pointer x) |
| { |
| if(x->next()->prior()==x)return pointer_from(x->next()); |
| if(x->next()->prior()->prior()==x)return x->next()->prior(); |
| if(x->next()->prior()->prior()->next()==base_pointer_from(x)) |
| return pointer_from(x->next()); |
| return pointer_from(x->next())->next()->prior(); |
| } |
| |
| static pointer after_local(pointer x) |
| { |
| if(x->next()->prior()==x)return pointer_from(x->next()); |
| if(x->next()->prior()->prior()==x)return pointer(0); |
| if(x->next()->prior()->prior()->next()==base_pointer_from(x)) |
| return pointer_from(x->next()); |
| return pointer_from(x->next())->next()->prior(); |
| } |
| |
| static pointer next_to_inspect(pointer x) |
| { |
| if(x->next()->prior()==x)return pointer_from(x->next()); |
| if(x->next()->prior()->prior()==x)return pointer(0); |
| if(x->next()->prior()->next()->prior()!=x->next()->prior()) |
| return pointer(0); |
| return pointer_from(x->next()->prior()->next()); |
| } |
| |
| static void link(pointer x,base_pointer buc,pointer end) |
| { |
| if(buc->prior()==pointer(0)){ /* empty bucket */ |
| x->prior()=end->prior(); |
| x->next()=end->prior()->next(); |
| x->prior()->next()=buc; |
| buc->prior()=x; |
| end->prior()=x; |
| } |
| else{ |
| x->prior()=buc->prior()->prior(); |
| x->next()=base_pointer_from(buc->prior()); |
| buc->prior()=x; |
| x->next()->prior()=x; |
| } |
| }; |
| |
| static void link(pointer x,pointer first,pointer last) |
| { |
| x->prior()=first->prior(); |
| x->next()=base_pointer_from(first); |
| if(is_first_of_bucket(first)){ |
| x->prior()->next()->prior()=x; |
| } |
| else{ |
| x->prior()->next()=base_pointer_from(x); |
| } |
| |
| if(first==last){ |
| last->prior()=x; |
| } |
| else if(first->next()==base_pointer_from(last)){ |
| first->prior()=last; |
| first->next()=base_pointer_from(x); |
| } |
| else{ |
| pointer second=pointer_from(first->next()), |
| lastbutone=last->prior(); |
| second->prior()=first; |
| first->prior()=last; |
| lastbutone->next()=base_pointer_from(x); |
| } |
| } |
| |
| static void unlink(pointer x) |
| { |
| default_assigner assign; |
| unlink(x,assign); |
| } |
| |
| typedef unlink_undo_assigner<Node> unlink_undo; |
| |
| template<typename Assigner> |
| static void unlink(pointer x,Assigner& assign) |
| { |
| if(x->prior()->next()==base_pointer_from(x)){ |
| if(x->next()->prior()==x){ |
| left_unlink(x,assign); |
| right_unlink(x,assign); |
| } |
| else if(x->next()->prior()->prior()==x){ /* last of bucket */ |
| left_unlink(x,assign); |
| right_unlink_last_of_bucket(x,assign); |
| } |
| else if(x->next()->prior()->prior()->next()== |
| base_pointer_from(x)){ /* first of group size */ |
| left_unlink(x,assign); |
| right_unlink_first_of_group(x,assign); |
| } |
| else{ /* n-1 of group */ |
| unlink_last_but_one_of_group(x,assign); |
| } |
| } |
| else if(x->prior()->next()->prior()==x){ /* first of bucket */ |
| if(x->next()->prior()==x){ |
| left_unlink_first_of_bucket(x,assign); |
| right_unlink(x,assign); |
| } |
| else if(x->next()->prior()->prior()==x){ /* last of bucket */ |
| assign(x->prior()->next()->prior(),pointer(0)); |
| assign(x->prior()->next(),x->next()); |
| assign(x->next()->prior()->prior(),x->prior()); |
| } |
| else{ /* first of group */ |
| left_unlink_first_of_bucket(x,assign); |
| right_unlink_first_of_group(x,assign); |
| } |
| } |
| else if(x->next()->prior()->prior()==x){ /* last of group and bucket */ |
| left_unlink_last_of_group(x,assign); |
| right_unlink_last_of_bucket(x,assign); |
| } |
| else if(pointer_from(x->prior()->prior()->next()) |
| ->next()==base_pointer_from(x)){ /* second of group */ |
| unlink_second_of_group(x,assign); |
| } |
| else{ /* last of group, ~(last of bucket) */ |
| left_unlink_last_of_group(x,assign); |
| right_unlink(x,assign); |
| } |
| } |
| |
| /* used only at rehashing */ |
| |
| static void link_range( |
| pointer first,pointer last,base_pointer buc,pointer cend) |
| { |
| if(buc->prior()==pointer(0)){ /* empty bucket */ |
| first->prior()=cend->prior(); |
| last->next()=cend->prior()->next(); |
| first->prior()->next()=buc; |
| buc->prior()=first; |
| cend->prior()=last; |
| } |
| else{ |
| first->prior()=buc->prior()->prior(); |
| last->next()=base_pointer_from(buc->prior()); |
| buc->prior()=first; |
| last->next()->prior()=last; |
| } |
| } |
| |
| static void append_range(pointer first,pointer last,pointer cend) |
| { |
| first->prior()=cend->prior(); |
| last->next()=cend->prior()->next(); |
| first->prior()->next()=base_pointer_from(first); |
| cend->prior()=last; |
| } |
| |
| static std::pair<pointer,bool> unlink_last_group(pointer end) |
| { |
| /* returns first of group true iff bucket is emptied */ |
| |
| pointer x=end->prior(); |
| if(x->prior()->next()==base_pointer_from(x)){ |
| x->prior()->next()=x->next(); |
| end->prior()=x->prior(); |
| return std::make_pair(x,false); |
| } |
| else if(x->prior()->next()->prior()==x){ |
| x->prior()->next()->prior()=pointer(0); |
| x->prior()->next()=x->next(); |
| end->prior()=x->prior(); |
| return std::make_pair(x,true); |
| } |
| else{ |
| pointer y=pointer_from(x->prior()->next()); |
| |
| if(y->prior()->next()==base_pointer_from(y)){ |
| y->prior()->next()=x->next(); |
| end->prior()=y->prior(); |
| return std::make_pair(y,false); |
| } |
| else{ |
| y->prior()->next()->prior()=pointer(0); |
| y->prior()->next()=x->next(); |
| end->prior()=y->prior(); |
| return std::make_pair(y,true); |
| } |
| } |
| } |
| |
| static void unlink_range(pointer first,pointer last) |
| { |
| if(is_first_of_bucket(first)){ |
| if(is_last_of_bucket(last)){ |
| first->prior()->next()->prior()=pointer(0); |
| first->prior()->next()=last->next(); |
| last->next()->prior()->prior()=first->prior(); |
| } |
| else{ |
| first->prior()->next()->prior()=pointer_from(last->next()); |
| last->next()->prior()=first->prior(); |
| } |
| } |
| else if(is_last_of_bucket(last)){ |
| first->prior()->next()=last->next(); |
| last->next()->prior()->prior()=first->prior(); |
| } |
| else{ |
| first->prior()->next()=last->next(); |
| last->next()->prior()=first->prior(); |
| } |
| } |
| |
| private: |
| static pointer pointer_from(base_pointer x) |
| { |
| return Node::pointer_from(x); |
| } |
| |
| static base_pointer base_pointer_from(pointer x) |
| { |
| return Node::base_pointer_from(x); |
| } |
| |
| static bool is_last_of_bucket(pointer x) |
| { |
| return x->next()->prior()->prior()==x; |
| } |
| |
| template<typename Assigner> |
| static void left_unlink(pointer x,Assigner& assign) |
| { |
| assign(x->prior()->next(),x->next()); |
| } |
| |
| template<typename Assigner> |
| static void right_unlink(pointer x,Assigner& assign) |
| { |
| assign(x->next()->prior(),x->prior()); |
| } |
| |
| template<typename Assigner> |
| static void left_unlink_first_of_bucket(pointer x,Assigner& assign) |
| { |
| assign(x->prior()->next()->prior(),pointer_from(x->next())); |
| } |
| |
| template<typename Assigner> |
| static void right_unlink_last_of_bucket(pointer x,Assigner& assign) |
| { |
| assign(x->next()->prior()->prior(),x->prior()); |
| } |
| |
| template<typename Assigner> |
| static void right_unlink_first_of_group(pointer x,Assigner& assign) |
| { |
| pointer second=pointer_from(x->next()), |
| last=second->prior(), |
| lastbutone=last->prior(); |
| if(second==lastbutone){ |
| assign(second->next(),base_pointer_from(last)); |
| assign(second->prior(),x->prior()); |
| } |
| else{ |
| assign(lastbutone->next(),base_pointer_from(second)); |
| assign(second->next()->prior(),last); |
| assign(second->prior(),x->prior()); |
| } |
| } |
| |
| template<typename Assigner> |
| static void left_unlink_last_of_group(pointer x,Assigner& assign) |
| { |
| pointer lastbutone=x->prior(), |
| first=pointer_from(lastbutone->next()), |
| second=pointer_from(first->next()); |
| if(lastbutone==second){ |
| assign(lastbutone->prior(),first); |
| assign(lastbutone->next(),x->next()); |
| } |
| else{ |
| assign(second->prior(),lastbutone); |
| assign(lastbutone->prior()->next(),base_pointer_from(first)); |
| assign(lastbutone->next(),x->next()); |
| } |
| } |
| |
| template<typename Assigner> |
| static void unlink_last_but_one_of_group(pointer x,Assigner& assign) |
| { |
| pointer first=pointer_from(x->next()), |
| second=pointer_from(first->next()), |
| last=second->prior(); |
| if(second==x){ |
| assign(last->prior(),first); |
| assign(first->next(),base_pointer_from(last)); |
| } |
| else{ |
| assign(last->prior(),x->prior()); |
| assign(x->prior()->next(),base_pointer_from(first)); |
| } |
| } |
| |
| template<typename Assigner> |
| static void unlink_second_of_group(pointer x,Assigner& assign) |
| { |
| pointer last=x->prior(), |
| lastbutone=last->prior(), |
| first=pointer_from(lastbutone->next()); |
| if(lastbutone==x){ |
| assign(first->next(),base_pointer_from(last)); |
| assign(last->prior(),first); |
| } |
| else{ |
| assign(first->next(),x->next()); |
| assign(x->next()->prior(),last); |
| } |
| } |
| }; |
| |
| template<typename Super> |
| struct hashed_index_node_trampoline: |
| hashed_index_node_impl< |
| typename boost::detail::allocator::rebind_to< |
| typename Super::allocator_type, |
| char |
| >::type |
| > |
| { |
| typedef typename boost::detail::allocator::rebind_to< |
| typename Super::allocator_type, |
| char |
| >::type impl_allocator_type; |
| typedef hashed_index_node_impl<impl_allocator_type> impl_type; |
| }; |
| |
| template<typename Super,typename Category> |
| struct hashed_index_node: |
| Super,hashed_index_node_trampoline<Super> |
| { |
| private: |
| typedef hashed_index_node_trampoline<Super> trampoline; |
| |
| public: |
| typedef typename trampoline::impl_type impl_type; |
| typedef hashed_index_node_alg< |
| impl_type,Category> node_alg; |
| typedef typename trampoline::base_pointer impl_base_pointer; |
| typedef typename trampoline::const_base_pointer const_impl_base_pointer; |
| typedef typename trampoline::pointer impl_pointer; |
| typedef typename trampoline::const_pointer const_impl_pointer; |
| |
| impl_pointer& prior(){return trampoline::prior();} |
| impl_pointer prior()const{return trampoline::prior();} |
| impl_base_pointer& next(){return trampoline::next();} |
| impl_base_pointer next()const{return trampoline::next();} |
| |
| impl_pointer impl() |
| { |
| return static_cast<impl_pointer>( |
| static_cast<impl_type*>(static_cast<trampoline*>(this))); |
| } |
| |
| const_impl_pointer impl()const |
| { |
| return static_cast<const_impl_pointer>( |
| static_cast<const impl_type*>(static_cast<const trampoline*>(this))); |
| } |
| |
| static hashed_index_node* from_impl(impl_pointer x) |
| { |
| return static_cast<hashed_index_node*>( |
| static_cast<trampoline*>(&*x)); |
| } |
| |
| static const hashed_index_node* from_impl(const_impl_pointer x) |
| { |
| return static_cast<const hashed_index_node*>( |
| static_cast<const trampoline*>(&*x)); |
| } |
| |
| /* interoperability with hashed_index_iterator */ |
| |
| static void increment(hashed_index_node*& x) |
| { |
| x=from_impl(node_alg::after(x->impl())); |
| } |
| |
| static void increment_local(hashed_index_node*& x) |
| { |
| x=from_impl(node_alg::after_local(x->impl())); |
| } |
| }; |
| |
| } /* namespace multi_index::detail */ |
| |
| } /* namespace multi_index */ |
| |
| } /* namespace boost */ |
| |
| #endif |