/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #ifndef CPPCMS_IMPL_HASH_MAP_H #define CPPCMS_IMPL_HASH_MAP_H #include #include #include #include #include #include #include namespace cppcms { namespace impl { struct string_hash { public: typedef uint32_t state_type; static const state_type initial_state = 0; static state_type update_state(state_type value,char c) { value = (value << 4) + static_cast(c); uint32_t high = (value & 0xF0000000U); if(high!=0) value = (value ^ (high >> 24)) ^ high; return value; } template size_t operator()(T const &v) const { state_type st = initial_state; for(typename T::const_iterator p=v.begin();p!=v.end();++p) { st = update_state(st,*p); } return st; } }; namespace details { struct are_equal { template bool operator()(T1 const &v1,T2 const &v2) const { return v1==v2; } }; template class intrusive_list { public: intrusive_list() : begin(0), end(0) { } void push_back(T *p) { p->next = 0; p->prev = end; if(end) end->next = p; end = p; if(begin == 0) begin = p; } void push_front(T *p) { p->next = begin; p->prev = 0; if(begin) begin->prev = p; begin = p; if(end == 0) end = p; } void erase(T *p) { if(p->prev) { p->prev->next = p->next; } if(p->next) { p->next->prev = p->prev; } if(begin == p) begin = p->next; if(end == p) end = p->prev; p->next = p->prev = 0; } void insert_after(T *p,T *after_me) { if(!after_me->next) { push_back(p); return; } p->prev = after_me; p->next = after_me->next; if(after_me->next) after_me->next->prev = p; after_me->next = p; } void insert_before(T *p,T *before_me) { if(!before_me->prev) { push_front(p); return; } p->next = before_me; p->prev = before_me->prev; if(before_me->prev) before_me->prev->next = p; before_me->prev = p; } void swap(intrusive_list &other) { std::swap(other.begin,begin); std::swap(other.end,end); } T *begin,*end; }; template< typename Key, typename Value, typename Hash, typename Equals=are_equal, typename Alloc= std::allocator > > class basic_map { public: basic_map() : size_(0) { } ~basic_map() { clear(); } typedef std::pair value_type; struct container { container() : val(), next(0),prev(0) {} container(value_type const &v) : val(v), next(0),prev(0) {} value_type val; size_t hash; container *next; container *prev; }; typedef container *iterator; typedef typename Alloc::template rebind::other container_alloc; typedef std::pair range_type; typedef typename Alloc::template rebind::other vec_allocator; iterator erase(iterator p) { if(p==0) return 0; range_type &r = get(p->val.first); if(r.first == r.second) { assert(p == r.first); r.first = r.second = 0; } else if(r.first == p) r.first = p->next; else if(r.second == p) r.second = p->prev; iterator next = p->next; list_.erase(p); size_ --; destroy(p); return next; } size_t size() const { return size_; } template iterator find(Kt const &k) { if(hash_.empty()) return 0; range_type &range=get(k); return find_in_range(range,k); } std::pair insert(value_type const &entry) { std::pair r(iterator(),false); rehash_if_needed(); range_type &range=get(entry.first); iterator p = find_in_range(range,entry.first); if(p) { r.first = p; return r; } p = allocate(entry); if(range.second == 0) { list_.push_back(p); range.first = range.second = p; } else { list_.insert_after(p,range.second); range.second = p; } r.first = p; r.second = true; size_ ++; return r; } void clear() { iterator p=list_.begin; if(size_ / 4 >= hash_.size()) { for(size_t i=0;inext; del->prev = 0; del->next = 0; destroy(del); } } else { while(p) { iterator del = p; p=p->next; del->prev = 0; del->next = 0; range_type &r=get(del->val.first); r.first = r.second = 0; destroy(del); } } list_.begin = list_.end = 0; size_ = 0; } iterator begin() { return list_.begin; } iterator end() { return 0; } #ifdef TEST_MAP void check() { size_t count = 0; for(iterator p=list_.begin;p!=0;p=p->next,count++) { range_type &r = get(p->val.first); if(r.first == r.second) { TEST(r.first==p); } bool found = false; iterator p2; for(p2=r.first;p2!=r.second;p2=p2->next) { if(p2 == p) { found = true; break; } } if(!found) TEST(p2 == p); if(p->next == 0) { TEST(p == list_.end); } } TEST(size_ == count); count = 0; for(size_t i=0;inext) { count++; Hash h; TEST(h(p->val.first) % hash_.size() == i); if(p==hash_[i].second) break; } } TEST(size_ == count); } #endif void rehash(size_t new_size) { basic_map tmp; tmp.hash_.resize(new_size,range_type(iterator(),iterator())); while(list_.begin) { iterator p=list_.begin; list_.erase(p); range_type &r = tmp.get(p->val.first); if(r.first == 0) { tmp.list_.push_back(p); r.first = r.second = p; } else { tmp.list_.insert_after(p,r.second); r.second = p; } } list_.swap(tmp.list_); hash_.swap(tmp.hash_); tmp.hash_.clear(); } private: template iterator find_in_range(range_type &r,Kt const &k) { for(iterator p=r.first;p!=0;p=p->next) { Equals compare; if(compare(p->val.first,k)) { return p; } if(p==r.second) return 0; } return 0; } template range_type &get(Kt const &k) { Hash hf; size_t h = hf(k) % hash_.size(); return hash_[h]; } size_t next_size() { return (1+size_)*2; } void rehash_if_needed() { size_t table_size = hash_.size(); if(size_ + 1 >= table_size) { rehash(next_size()); } } iterator allocate(value_type const &v) { container_alloc al; iterator p = al.allocate(1); try { new (p) container(v); } catch(...) { al.deallocate(p,1); throw; } return p; } iterator allocate() { container_alloc al; iterator p = al.allocate(1); try { new (p) container(); } catch(...) { al.deallocate(p); throw; } return p; } void destroy(iterator p) { container_alloc al; p->~container(); al.deallocate(p,1); } std::vector hash_; intrusive_list list_; size_t size_; }; } // details template< typename Key, typename Value, typename Hash, typename Equals = details::are_equal, typename Alloc= std::allocator > > class hash_map { typedef details::basic_map impl_type; typedef typename impl_type::iterator impl_iterator; public: typedef std::pair value_type; #ifdef TEST_MAP void check() { impl_.check(); } #endif void rehash(size_t n) { impl_.rehash(n); } ~hash_map() { clear(); } class iterator : public booster::iterator_facade< iterator, value_type, booster::bidirectional_traversal_tag> { public: iterator(typename impl_type::iterator p = 0) : p_(p) {} value_type &dereference() const { return p_->val; } void increment() { p_=p_->next; } void decrement() { p_=p_->prev; } bool equal(iterator const &other) const { return p_==other.p_; } private: friend class hash_map; typename impl_type::iterator p_; }; iterator begin() { return iterator(impl_.begin()); } iterator end() { return iterator(0); } template iterator find(Kt const &k) { return iterator(impl_.find(k)); } std::pair insert(value_type const &v) { std::pair r = impl_.insert(v); return std::pair(iterator(r.first),r.second); } iterator erase(iterator p) { return iterator(impl_.erase(p.p_)); } void clear() { impl_.clear(); } size_t size() const { return impl_.size(); } private: impl_type impl_; }; } // impl } // cppcms #endif