CMS 3D CMS Logo

OtherHashMaps.h
Go to the documentation of this file.
1 #ifndef TrackingTools_TrajectoryCleaning_src_OtherHashMaps
2 #define TrackingTools_TrajectoryCleaning_src_OtherHashMaps
3 
4 #include <map>
5 #include <vector>
6 #include <memory>
7 #include <list>
8 #include <iterator>
9 #include <boost/unordered_map.hpp>
10 #include <boost/range.hpp>
11 #include <boost/type_traits.hpp>
12 #include <boost/mpl/and.hpp>
13 #include <boost/mpl/logical.hpp>
14 #include <boost/mpl/assert.hpp>
15 #include <boost/mpl/if.hpp>
16 
17 #include <iostream>
18 
19 namespace cmsutil {
20 
21  /*** The concept is the same of std::map<K, std::vector<V>>, but the implementation is much different (and the interface is not really compatible)
22  * It works only for K and V objects with trivial destructors (primitive types and bare pointers are fine)
23  * The main implementation difference w.r.t. unordered_map<K, std::vector<V>> is that this map is optimized to do very few memory allocations
24  * (in particular, clear does not redeme memory so if you just clear the map instead of deleting it you won't call malloc each time you fill it)
25  * The map doesn't rehash, so you should set a reasonable number of buckets (that you can change also when clearing the map)
26  * Note that too many buckets will make your map slow to clear (and possibly more prone to cache misses).
27  * Although it can take an allocator as template argument, it has been tested only with std::allocator.
28  * When used in the TrajectoryCleanerBySharedHits it works and it does improve the performance. Any other usecase was not checked at all.
29  * */
30  template <typename K,
31  typename V,
32  typename Hasher = boost::hash<K>,
33  typename Equals = std::equal_to<K>,
34  typename Alloc = std::allocator<V> >
36  // taking Alloc definition from http://www.codeproject.com/KB/cpp/allocator.aspx
37  BOOST_MPL_ASSERT((boost::mpl::and_<boost::has_trivial_destructor<K>, boost::has_trivial_destructor<V> >));
38 
39  public:
40  typedef Hasher hasher;
42  typedef typename boost::mpl::if_<typename boost::is_pointer<V>, V, V const &>::type
43  value_ref; // otherwise we get problems with 'const' if V is a pointer
44 
45  SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows = 50);
46 
48 
49  void clear(size_t newBucketSize = 0) {
50  if (newBucketSize != 0) {
51  if (newBucketSize > bucketCapacity_) {
52  ptrAlloc_.deallocate(buckets_, bucketCapacity_);
53  bucketCapacity_ = newBucketSize;
55  }
56  bucketSize_ = newBucketSize;
57  }
58  memset(buckets_, 0, bucketSize_ * sizeof(KeyItem *));
59  currentKeyRow_ = keyRows_.begin();
60  nextKeyItem_ = keyRows_.front();
62  currentValueRow_ = valueRows_.begin();
63  nextValueItem_ = valueRows_.front();
65  if ((keyRows_.size() > maxRows_) || (valueRows_.size() > maxRows_))
66  freeRows();
67  }
68  void freeRows();
69 
70  bool empty() const { return nextKeyItem_ == keyRows_.front(); }
71 
72  void insert(K const &key, value_ref value);
73 
74  struct ValueItem {
75  ValueItem(ValueItem *next1, value_ref val) : next(next1), value(val) {}
77  V value;
78  typedef V value_type;
79  const value_type &operator()() const { return value; }
80  };
81 
82  struct KeyItem {
83  KeyItem(KeyItem *next1, K const &key1, ValueItem *val1) : key(key1), next(next1), value(val1) {}
84  K key;
88  const value_type &operator()() const { return *this; }
89  };
90 
91  template <typename Item>
92  class item_iterator {
93  public:
94  typedef ::std::forward_iterator_tag iterator_category;
95  typedef const typename Item::value_type value_type;
96  typedef const value_type &reference;
97  typedef const value_type *pointer;
98  typedef ptrdiff_t difference_type;
100 
101  item_iterator() : it_(nullptr) {}
102  item_iterator(const Item *it) : it_(it) {}
103  const value_type &operator*() const { return (*it_)(); }
104  const value_type *operator->() const { return &(*it_)(); }
105  self_type &operator++() {
106  if (it_ != nullptr)
107  it_ = it_->next;
108  return *this;
109  }
110  bool operator==(const self_type &other) const { return it_ == other.it_; }
111  bool operator!=(const self_type &other) const { return it_ != other.it_; }
112  bool good() const { return (it_ != nullptr); }
113 
114  private:
115  const Item *it_;
116  };
118 
119  value_iterator values(K const &key);
120 
121  private:
125 
126  // --- buckets ---
129  // --- keys ---
130  size_t keyRowSize_;
131  std::list<KeyItem *> keyRows_;
132  typename std::list<KeyItem *>::iterator
133  currentKeyRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
135  // --- values ---
137  std::list<ValueItem *> valueRows_;
138  typename std::list<ValueItem *>::iterator
139  currentValueRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
141  // --- other ---
142  size_t maxRows_;
143  Hasher hasher_;
144  Equals eq_;
145  KeyItemAllocator keyAlloc_;
146  ValueItemAllocator valueAlloc_;
147  KeyItemPtrAllocator ptrAlloc_;
148 
149  KeyItem *push_back_(K const &key, KeyItem *next);
151  KeyItem &find_or_insert_(K const &key);
152 
153  public:
154  void dump() {
155  std::cout << "Dumping HASH MULTIMAP" << std::endl;
156  std::cout << " Key items: "
157  << (std::distance(keyRows_.begin(), currentKeyRow_) * keyRowSize_ + (nextKeyItem_ - *currentKeyRow_))
158  << std::endl;
159  std::cout << " Value items: "
160  << (std::distance(valueRows_.begin(), currentValueRow_) * valueRowSize_ +
161  (nextValueItem_ - *currentValueRow_))
162  << std::endl;
163  size_t row = 0;
164  std::cout << " Buckets (size " << bucketSize_ << ", capacity " << bucketCapacity_ << ")";
165  for (KeyItem **p = buckets_; p != buckets_ + bucketSize_; ++p, ++row) {
166  std::cout << " [" << row << "] " << *p << std::endl;
167  }
168  row = 0;
169  std::cout << " Key Items " << std::endl;
170  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last;
171  ++it, ++row) {
172  KeyItem *lastI = *it + keyRowSize_;
173  std::cout << " Key Row " << row << " (of size " << keyRowSize_ << ")" << std::endl;
174  for (KeyItem *p = *it; p != lastI; ++p) {
175  std::cout << " @ " << p << " [" << p->key << ", @" << p->value << "], next = " << p->next << std::endl;
176  if ((it == currentKeyRow_) && (p == nextKeyItem_ - 1)) {
177  std::cout << " ^^^ this was the last valid item." << std::endl;
178  last = 0;
179  break;
180  }
181  }
182  if (lastI == 0)
183  break;
184  }
185  row = 0;
186  std::cout << " Value Items " << std::endl;
187  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last;
188  ++it, ++row) {
189  ValueItem *lastI = *it + valueRowSize_;
190  std::cout << " Value Row " << row << " (of size " << valueRowSize_ << ")" << std::endl;
191  for (ValueItem *p = *it; p != lastI; ++p) {
192  std::cout << " @ " << p << " [" << p->value << "], next = " << p->next << std::endl;
193  if ((it == currentValueRow_) && (p == nextValueItem_ - 1)) {
194  std::cout << " ^^^ this was the last valid item." << std::endl;
195  last = 0;
196  break;
197  }
198  }
199  if (lastI == 0)
200  break;
201  }
202  std::cout << " End of dump" << std::endl;
203  }
204  };
205 
206  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
208  size_t keyRowSize,
209  size_t valueRowSize,
210  size_t maxRows)
211  : bucketSize_(buckets),
213  keyRowSize_(keyRowSize),
214  valueRowSize_(valueRowSize),
215  maxRows_(maxRows) {
216  buckets_ = ptrAlloc_.allocate(bucketCapacity_);
217  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
218  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
219  clear();
220  }
221  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
223  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last; ++it) {
224  keyAlloc_.deallocate(*it, keyRowSize_);
225  }
226  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last; ++it) {
227  valueAlloc_.deallocate(*it, valueRowSize_);
228  }
229  ptrAlloc_.deallocate(buckets_, bucketCapacity_);
230  }
231 
232  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
234  if (keyRows_.size() > maxRows_) {
235  //std::cerr << "Freeing key rows, current size is " << keyRows_.size() << std::endl;
236  typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end();
237  for (std::advance(it, maxRows_); it != last; ++it) {
238  keyAlloc_.deallocate(*it, keyRowSize_);
239  }
240  keyRows_.resize(maxRows_);
241  }
242  if (valueRows_.size() > maxRows_) {
243  //std::cerr << "Freeing value rows, current size is " << valueRows_.size() << std::endl;
244  typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end();
245  for (std::advance(it, maxRows_); it != last; ++it) {
246  valueAlloc_.deallocate(*it, valueRowSize_);
247  }
248  valueRows_.resize(maxRows_);
249  }
250  }
251 
252  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
255  if (nextKeyItem_ == keyEndMarker_) {
256  ++currentKeyRow_;
257  if (currentKeyRow_ == keyRows_.end()) {
258  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
259  currentKeyRow_ = keyRows_.end();
260  --currentKeyRow_; // end - 1 doesn't work!
261  }
264  }
265  keyAlloc_.construct(nextKeyItem_, KeyItem(next, key, nullptr));
266  nextKeyItem_++;
267  return (nextKeyItem_ - 1);
268  }
269  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
274  if (currentValueRow_ == valueRows_.end()) {
275  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
277  --currentValueRow_; // end - 1 doesn't work!
278  }
281  }
282  valueAlloc_.construct(nextValueItem_, ValueItem(next, value));
283  nextValueItem_++;
284  return (nextValueItem_ - 1);
285  }
286 
287  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
290  //std::cout << "Find or insert for key " << key << std::endl;
291  size_t hash = hasher_(key);
292  KeyItem *&buck = buckets_[hash % bucketSize_];
293  KeyItem *curr = buck;
294  while (curr) {
295  if (eq_(curr->key, key)) {
296  //std::cout << " Key " << key << " was found." << std::endl;
297  return *curr;
298  }
299  curr = curr->next;
300  }
301  buck = push_back_(key, buck);
302  return *buck;
303  }
304 
305  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
307  //std::cout << "Pushing back value " << value << " for key " << key << std::endl;
308  KeyItem &k = find_or_insert_(key);
309  //std::cout << "Key " << (k.value ? "exists" : " is new") << std::endl;
310  k.value = push_back_(value, k.value);
311  }
312 
313  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
316  //std::cout << "Gettinv values for key " << key << std::endl;
317  size_t hash = hasher_(key);
318  for (KeyItem *curr = buckets_[hash % bucketSize_]; curr; curr = curr->next) {
319  if (eq_(curr->key, key))
320  return value_iterator(curr->value);
321  }
322  return value_iterator();
323  }
324 
325  /*** Very very simple map implementation
326  * It's just a std::vector<pair<key,value>>, and the operator[] does a linear search to find the key (it's O(N) time, both if the key exists and if it doesn't)
327  * Anyway, if your map is very small and if you clear it often, it performs better than more complex variants
328  * Semantics is as std::map, except that only very few methods are implemented.
329  * */
330  template <typename K, typename V>
332  public:
333  typedef std::pair<K, V> value_type;
334  typedef typename std::vector<value_type>::const_iterator const_iterator;
335  typedef typename std::vector<value_type>::iterator iterator; // but please don't mutate the keys
336 
337  void clear() { data_.clear(); }
338  bool empty() const { return data_.empty(); }
339  const_iterator begin() const { return data_.begin(); }
340  const_iterator end() const { return data_.end(); }
341  iterator begin() { return data_.begin(); }
342  iterator end() { return data_.end(); }
343 
344  V &operator[](const K &k) {
345  for (typename std::vector<value_type>::iterator it = data_.begin(), ed = data_.end(); it != ed; ++it) {
346  if (it->first == k)
347  return it->second;
348  }
349  data_.push_back(value_type(k, V()));
350  return data_.back().second;
351  }
352 
354 
355  private:
356  std::vector<value_type> data_;
357  };
358 
359 } // namespace cmsutil
360 
361 #endif
type
Definition: HCALResponse.h:21
item_iterator< ValueItem > value_iterator
std::list< ValueItem * > valueRows_
KeyItem & find_or_insert_(K const &key)
#define nullptr
const_iterator begin() const
const_iterator end() const
Alloc::template rebind< KeyItem >::other KeyItemAllocator
bool operator!=(const self_type &other) const
BOOST_MPL_ASSERT((boost::mpl::and_< boost::has_trivial_destructor< K >, boost::has_trivial_destructor< V > >))
SimpleAllocHashMultiMap< K, V, Hasher, Equals, Alloc > map_type
Definition: OtherHashMaps.h:41
::std::forward_iterator_tag iterator_category
Definition: OtherHashMaps.h:94
std::list< ValueItem * >::iterator currentValueRow_
KeyItem * push_back_(K const &key, KeyItem *next)
ValueItem(ValueItem *next1, value_ref val)
Definition: OtherHashMaps.h:75
def template(fileName, svg, replaceme="REPLACEME")
Definition: svgfig.py:521
void insert(K const &key, value_ref value)
Definition: value.py:1
value_iterator values(K const &key)
boost::mpl::if_< typename boost::is_pointer< V >, V, V const & >::type value_ref
Definition: OtherHashMaps.h:43
Alloc::template rebind< ValueItem >::other ValueItemAllocator
std::vector< value_type >::const_iterator const_iterator
const value_type & operator()() const
Definition: OtherHashMaps.h:79
const value_type & operator()() const
Definition: OtherHashMaps.h:88
std::vector< value_type > data_
std::vector< value_type >::iterator iterator
Alloc::template rebind< KeyItem * >::other KeyItemPtrAllocator
bool operator==(const self_type &other) const
void clear(size_t newBucketSize=0)
Definition: OtherHashMaps.h:49
std::list< KeyItem * > keyRows_
KeyItem(KeyItem *next1, K const &key1, ValueItem *val1)
Definition: OtherHashMaps.h:83
SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows=50)
std::list< KeyItem * >::iterator currentKeyRow_