Home | History | Annotate | Download | only in detail
      1 //
      2 // detail/hash_map.hpp
      3 // ~~~~~~~~~~~~~~~~~~~
      4 //
      5 // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
      6 //
      7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
      8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
      9 //
     10 
     11 #ifndef ASIO_DETAIL_HASH_MAP_HPP
     12 #define ASIO_DETAIL_HASH_MAP_HPP
     13 
     14 
     15 #include "asio/detail/config.hpp"
     16 #include <list>
     17 #include <utility>
     18 #include "asio/detail/assert.hpp"
     19 #include "asio/detail/noncopyable.hpp"
     20 
     21 
     22 #include "asio/detail/push_options.hpp"
     23 
     24 namespace asio {
     25 namespace detail {
     26 
     27 inline std::size_t calculate_hash_value(int i)
     28 {
     29   return static_cast<std::size_t>(i);
     30 }
     31 
     32 inline std::size_t calculate_hash_value(void* p)
     33 {
     34   return reinterpret_cast<std::size_t>(p)
     35     + (reinterpret_cast<std::size_t>(p) >> 3);
     36 }
     37 
     38 
     39 // Note: assumes K and V are POD types.
     40 template <typename K, typename V>
     41 class hash_map
     42   : private noncopyable
     43 {
     44 public:
     45   // The type of a value in the map.
     46   typedef std::pair<K, V> value_type;
     47 
     48   // The type of a non-const iterator over the hash map.
     49   typedef typename std::list<value_type>::iterator iterator;
     50 
     51   // The type of a const iterator over the hash map.
     52   typedef typename std::list<value_type>::const_iterator const_iterator;
     53 
     54   // Constructor.
     55   hash_map()
     56     : size_(0),
     57       buckets_(0),
     58       num_buckets_(0)
     59   {
     60   }
     61 
     62   // Destructor.
     63   ~hash_map()
     64   {
     65     delete[] buckets_;
     66   }
     67 
     68   // Get an iterator for the beginning of the map.
     69   iterator begin()
     70   {
     71     return values_.begin();
     72   }
     73 
     74   // Get an iterator for the beginning of the map.
     75   const_iterator begin() const
     76   {
     77     return values_.begin();
     78   }
     79 
     80   // Get an iterator for the end of the map.
     81   iterator end()
     82   {
     83     return values_.end();
     84   }
     85 
     86   // Get an iterator for the end of the map.
     87   const_iterator end() const
     88   {
     89     return values_.end();
     90   }
     91 
     92   // Check whether the map is empty.
     93   bool empty() const
     94   {
     95     return values_.empty();
     96   }
     97 
     98   // Find an entry in the map.
     99   iterator find(const K& k)
    100   {
    101     if (num_buckets_)
    102     {
    103       size_t bucket = calculate_hash_value(k) % num_buckets_;
    104       iterator it = buckets_[bucket].first;
    105       if (it == values_.end())
    106         return values_.end();
    107       iterator end_it = buckets_[bucket].last;
    108       ++end_it;
    109       while (it != end_it)
    110       {
    111         if (it->first == k)
    112           return it;
    113         ++it;
    114       }
    115     }
    116     return values_.end();
    117   }
    118 
    119   // Find an entry in the map.
    120   const_iterator find(const K& k) const
    121   {
    122     if (num_buckets_)
    123     {
    124       size_t bucket = calculate_hash_value(k) % num_buckets_;
    125       const_iterator it = buckets_[bucket].first;
    126       if (it == values_.end())
    127         return it;
    128       const_iterator end_it = buckets_[bucket].last;
    129       ++end_it;
    130       while (it != end_it)
    131       {
    132         if (it->first == k)
    133           return it;
    134         ++it;
    135       }
    136     }
    137     return values_.end();
    138   }
    139 
    140   // Insert a new entry into the map.
    141   std::pair<iterator, bool> insert(const value_type& v)
    142   {
    143     if (size_ + 1 >= num_buckets_)
    144       rehash(hash_size(size_ + 1));
    145     size_t bucket = calculate_hash_value(v.first) % num_buckets_;
    146     iterator it = buckets_[bucket].first;
    147     if (it == values_.end())
    148     {
    149       buckets_[bucket].first = buckets_[bucket].last =
    150         values_insert(values_.end(), v);
    151       ++size_;
    152       return std::pair<iterator, bool>(buckets_[bucket].last, true);
    153     }
    154     iterator end_it = buckets_[bucket].last;
    155     ++end_it;
    156     while (it != end_it)
    157     {
    158       if (it->first == v.first)
    159         return std::pair<iterator, bool>(it, false);
    160       ++it;
    161     }
    162     buckets_[bucket].last = values_insert(end_it, v);
    163     ++size_;
    164     return std::pair<iterator, bool>(buckets_[bucket].last, true);
    165   }
    166 
    167   // Erase an entry from the map.
    168   void erase(iterator it)
    169   {
    170     ASIO_ASSERT(it != values_.end());
    171     ASIO_ASSERT(num_buckets_ != 0);
    172 
    173     size_t bucket = calculate_hash_value(it->first) % num_buckets_;
    174     bool is_first = (it == buckets_[bucket].first);
    175     bool is_last = (it == buckets_[bucket].last);
    176     if (is_first && is_last)
    177       buckets_[bucket].first = buckets_[bucket].last = values_.end();
    178     else if (is_first)
    179       ++buckets_[bucket].first;
    180     else if (is_last)
    181       --buckets_[bucket].last;
    182 
    183     values_erase(it);
    184     --size_;
    185   }
    186 
    187   // Erase a key from the map.
    188   void erase(const K& k)
    189   {
    190     iterator it = find(k);
    191     if (it != values_.end())
    192       erase(it);
    193   }
    194 
    195   // Remove all entries from the map.
    196   void clear()
    197   {
    198     // Clear the values.
    199     values_.clear();
    200     size_ = 0;
    201 
    202     // Initialise all buckets to empty.
    203     iterator end_it = values_.end();
    204     for (size_t i = 0; i < num_buckets_; ++i)
    205       buckets_[i].first = buckets_[i].last = end_it;
    206   }
    207 
    208 private:
    209   // Calculate the hash size for the specified number of elements.
    210   static std::size_t hash_size(std::size_t num_elems)
    211   {
    212     static std::size_t sizes[] =
    213     {
    214 #if defined(ASIO_HASH_MAP_BUCKETS)
    215       ASIO_HASH_MAP_BUCKETS
    216 #else // ASIO_HASH_MAP_BUCKETS
    217       3, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
    218       49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
    219       12582917, 25165843
    220 #endif // ASIO_HASH_MAP_BUCKETS
    221     };
    222     const std::size_t nth_size = sizeof(sizes) / sizeof(std::size_t) - 1;
    223     for (std::size_t i = 0; i < nth_size; ++i)
    224       if (num_elems < sizes[i])
    225         return sizes[i];
    226     return sizes[nth_size];
    227   }
    228 
    229   // Re-initialise the hash from the values already contained in the list.
    230   void rehash(std::size_t num_buckets)
    231   {
    232     if (num_buckets == num_buckets_)
    233       return;
    234     num_buckets_ = num_buckets;
    235     ASIO_ASSERT(num_buckets_ != 0);
    236 
    237     iterator end_iter = values_.end();
    238 
    239     // Update number of buckets and initialise all buckets to empty.
    240     bucket_type* tmp = new bucket_type[num_buckets_];
    241     delete[] buckets_;
    242     buckets_ = tmp;
    243     for (std::size_t i = 0; i < num_buckets_; ++i)
    244       buckets_[i].first = buckets_[i].last = end_iter;
    245 
    246     // Put all values back into the hash.
    247     iterator iter = values_.begin();
    248     while (iter != end_iter)
    249     {
    250       std::size_t bucket = calculate_hash_value(iter->first) % num_buckets_;
    251       if (buckets_[bucket].last == end_iter)
    252       {
    253         buckets_[bucket].first = buckets_[bucket].last = iter++;
    254       }
    255       else if (++buckets_[bucket].last == iter)
    256       {
    257         ++iter;
    258       }
    259       else
    260       {
    261         values_.splice(buckets_[bucket].last, values_, iter++);
    262         --buckets_[bucket].last;
    263       }
    264     }
    265   }
    266 
    267   // Insert an element into the values list by splicing from the spares list,
    268   // if a spare is available, and otherwise by inserting a new element.
    269   iterator values_insert(iterator it, const value_type& v)
    270   {
    271     if (spares_.empty())
    272     {
    273       return values_.insert(it, v);
    274     }
    275     else
    276     {
    277       spares_.front() = v;
    278       values_.splice(it, spares_, spares_.begin());
    279       return --it;
    280     }
    281   }
    282 
    283   // Erase an element from the values list by splicing it to the spares list.
    284   void values_erase(iterator it)
    285   {
    286     *it = value_type();
    287     spares_.splice(spares_.begin(), values_, it);
    288   }
    289 
    290   // The number of elements in the hash.
    291   std::size_t size_;
    292 
    293   // The list of all values in the hash map.
    294   std::list<value_type> values_;
    295 
    296   // The list of spare nodes waiting to be recycled. Assumes that POD types only
    297   // are stored in the hash map.
    298   std::list<value_type> spares_;
    299 
    300   // The type for a bucket in the hash table.
    301   struct bucket_type
    302   {
    303     iterator first;
    304     iterator last;
    305   };
    306 
    307   // The buckets in the hash.
    308   bucket_type* buckets_;
    309 
    310   // The number of buckets in the hash.
    311   std::size_t num_buckets_;
    312 };
    313 
    314 } // namespace detail
    315 } // namespace asio
    316 
    317 #include "asio/detail/pop_options.hpp"
    318 
    319 #endif // ASIO_DETAIL_HASH_MAP_HPP
    320