Adobe Source Libraries 1.49.0
A collection of C++ libraries.
Loading...
Searching...
No Matches
closed_hash.hpp
Go to the documentation of this file.
1/*
2 Copyright 2013 Adobe
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5*/
6/**************************************************************************************************/
7
8#ifndef ADOBE_CLOSED_HASH_HPP
9#define ADOBE_CLOSED_HASH_HPP
10
11/**************************************************************************************************/
12
13#include <adobe/config.hpp>
14
16
17#include <climits>
18#include <cstddef>
19#include <cstdint>
20#include <initializer_list>
21#include <limits>
22#include <utility>
23
24#include <boost/compressed_pair.hpp>
25#include <boost/iterator/iterator_adaptor.hpp>
26#include <boost/iterator/iterator_facade.hpp>
27#include <boost/next_prior.hpp>
28#include <boost/operators.hpp>
29
31#include <adobe/conversion.hpp>
32#include <adobe/empty.hpp>
33#include <adobe/functional.hpp>
35#include <adobe/memory.hpp>
36#include <adobe/type_traits.hpp>
37#include <adobe/utility.hpp>
38
39#include <adobe/implementation/swap.hpp>
40
41/**************************************************************************************************/
42
43namespace adobe {
44
45/**************************************************************************************************/
46
47namespace implementation {
48
49/**************************************************************************************************/
50
51template <typename T, typename V> // V is value_type(T) const qualified
52class closed_hash_iterator : public boost::iterator_facade<closed_hash_iterator<T, V>, V,
53 std::bidirectional_iterator_tag> {
54 typedef boost::iterator_facade<closed_hash_iterator<T, V>, V, std::bidirectional_iterator_tag>
55 inherited_t;
56
57 typedef typename T::node_t node_t;
58
59public:
60 typedef typename inherited_t::reference reference;
61 typedef typename inherited_t::difference_type difference_type;
62 typedef typename inherited_t::value_type value_type;
63
64 closed_hash_iterator() : node_m(0) {}
65
66 template <typename O>
67 closed_hash_iterator(const closed_hash_iterator<T, O>& x) : node_m(x.node_m) {}
68
69public:
70 /*
71 REVISIT (sparent@adobe.com) : node_m should be private but
72 "gcc version 4.0.1 (Apple Inc. build 5465)" doesn't like it.
73 */
74
75 node_t* node_m;
76
77private:
78 reference dereference() const { return node_m->value_m; }
79 void increment() { node_m = node_m->next(); }
80 void decrement() { node_m = node_m->prior(); }
81
82 template <typename O>
83 bool equal(const closed_hash_iterator<T, O>& y) const {
84 return node_m == y.node_m;
85 }
86
87 std::size_t state() const { return node_m->state(); }
88 void set_state(std::size_t x) { return node_m->set_state(x); }
89
90 explicit closed_hash_iterator(node_t* node) : node_m(node) {}
91
92 friend class version_1::closed_hash_set<value_type, typename T::key_transform,
93 typename T::hasher, typename T::key_equal,
94 typename T::allocator_type>;
95 friend class boost::iterator_core_access;
96 friend struct unsafe::set_next_fn<closed_hash_iterator>;
97};
98
99/**************************************************************************************************/
100
101} // namespace implementation
102
103/**************************************************************************************************/
104
105namespace unsafe {
106
107template <typename T, typename V>
108struct set_next_fn<implementation::closed_hash_iterator<T, V>> {
109 typedef typename implementation::closed_hash_iterator<T, V> iterator;
110
111 void operator()(iterator x, iterator y) const { set_next(*x.node_m, *y.node_m); }
112};
113
114} // namespace unsafe
115
116/**************************************************************************************************/
117
118#ifndef ADOBE_NO_DOCUMENTATION
119
120inline namespace version_1 {
121
122#endif
123
124/**************************************************************************************************/
125
144
145template <typename T, typename KeyTransform, typename Hash, typename Pred, typename A>
147 : boost::equality_comparable<closed_hash_set<T, KeyTransform, Hash, Pred, A>,
148 closed_hash_set<T, KeyTransform, Hash, Pred, A>,
149 empty_base<closed_hash_set<T, KeyTransform, Hash, Pred, A>>> {
150public:
151 typedef KeyTransform key_transform;
152
153 using key_type = std::remove_reference_t<adobe::invoke_result_t<KeyTransform, T>>;
154
155 typedef T value_type;
156 typedef Hash hasher;
157 typedef Pred key_equal;
158 typedef A allocator_type;
160 typedef const value_type* const_pointer;
163 typedef std::size_t size_type;
164 typedef std::ptrdiff_t difference_type;
165
166 friend class implementation::closed_hash_iterator<closed_hash_set, value_type>;
167 friend class implementation::closed_hash_iterator<closed_hash_set, const value_type>;
168
169 typedef implementation::closed_hash_iterator<closed_hash_set, value_type> iterator;
170 typedef implementation::closed_hash_iterator<closed_hash_set, const value_type> const_iterator;
171
172 typedef std::reverse_iterator<iterator> reverse_iterator;
173 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
174
175private:
176 enum { state_free = 0, state_home = 1, state_misplaced = 2 };
177
178 template <typename U> // U is derived node
179 struct list_node_base {
180 list_node_base() {
181 next_m = static_cast<U*>(this);
182 prior_m = static_cast<U*>(this);
183 }
184
185 U* address() { return static_cast<U*>(this); }
186 const U* address() const { return static_cast<const U*>(this); }
187
188 operator U&() { return *static_cast<U*>(this); }
189 operator const U&() const { return *static_cast<const U*>(this); }
190
191 friend inline void set_next(U& x, U& y) {
192 x.next_m = reinterpret_cast<U*>(std::uintptr_t(&y) | std::uintptr_t(x.state()));
193 y.prior_m = &x;
194 }
195
196 friend inline void set_next_raw(U& x, U& y) {
197 x.next_m = &y;
198 y.prior_m = &x;
199 }
200
201 std::size_t state() const {
202 return std::size_t(std::uintptr_t(next_m) & std::uintptr_t(0x03UL));
203 }
204 void set_state(std::size_t x) {
205 assert(x < 0x04UL);
206 next_m = reinterpret_cast<U*>(std::uintptr_t(next()) | std::uintptr_t(x));
207 }
208
209 U* next() const {
210 return reinterpret_cast<U*>(reinterpret_cast<std::uintptr_t>(next_m) &
211 ~std::uintptr_t(0x03UL));
212 }
213 U* prior() const { return prior_m; }
214
215 private:
216 U* next_m;
217 U* prior_m;
218 };
219
220 struct node_t : list_node_base<node_t> {
221 T value_m;
222 };
223
224 typedef list_node_base<node_t> node_base_t;
225
226 struct header_t {
228 boost::compressed_pair<allocator_type, node_base_t> alloc_free_tail_m;
229 node_base_t used_tail_m;
230 std::size_t capacity_m;
231 std::size_t size_m;
232 };
233
234 /*
235 NOTE (sparent) - the assumption is that the initial items are pointers and that size_t
236 is
237 either equal to the sizeof a pointer or a lower power of two so this packs tightly.
238 */
239
240 static_assert(!(sizeof(A) == sizeof(void*) || sizeof(A) == 0) ||
241 (sizeof(compact_header_t) ==
242 (sizeof(allocator_type) + 2 * sizeof(node_base_t) +
243 2 * sizeof(std::size_t))));
244
246 node_t storage_m[1];
247
248 allocator_type& allocator() { return header_m.get().alloc_free_tail_m.first(); }
249 const allocator_type& allocator() const { return header_m.get().alloc_free_tail_m.first(); }
250 node_base_t& free_tail() { return header_m.get().alloc_free_tail_m.second(); }
251 const node_base_t& free_tail() const { return header_m.get().alloc_free_tail_m.second(); }
252 node_base_t& used_tail() { return header_m.get().used_tail_m; }
253 const node_base_t& used_tail() const { return header_m.get().used_tail_m; }
254 std::size_t& capacity() { return header_m.get().capacity_m; }
255 const std::size_t& capacity() const { return header_m.get().capacity_m; }
256 std::size_t& size() { return header_m.get().size_m; }
257 const std::size_t& size() const { return header_m.get().size_m; }
258 };
259
260 typedef node_t* node_ptr;
261
262 typedef boost::compressed_pair<
263 hasher, boost::compressed_pair<key_equal, boost::compressed_pair<key_transform, header_t*>>>
264 data_t;
265
266 data_t data_m;
267
268 typedef header_t* header_pointer;
269
270 const header_pointer& header() const { return data_m.second().second().second(); }
271 header_pointer& header() { return data_m.second().second().second(); }
272
273public:
274 // construct/destroy/copy
275
276 closed_hash_set() { header() = 0; }
277
279 header() = 0;
280 allocate(allocator_type(), n);
281 }
282
284 const key_transform& kf = key_transform(),
285 const allocator_type& a = allocator_type()) {
286 header() = 0;
287 data_m.first() = hf;
288 data_m.second().first() = eq;
289 data_m.second().second().first() = kf;
290 allocate(a, n);
291 }
292
293 template <typename I> // I models InputIterator
294 closed_hash_set(I f, I l) {
295 header() = 0;
296 insert(f, l);
297 }
298
299 closed_hash_set(std::initializer_list<value_type> init) {
300 header() = 0;
301 insert(init.begin(), init.end());
302 }
303
304 template <typename I> // I models InputIterator
305 closed_hash_set(I f, I l, size_type n, const hasher& hf = hasher(),
306 const key_equal& eq = key_equal(), const key_transform& kf = key_transform(),
307 const allocator_type& a = allocator_type()) {
308 header() = 0;
309 data_m.first() = hf;
310 data_m.second().first() = eq;
311 data_m.second().second().first() = kf;
312 allocate(a, n);
313 insert(f, l);
314 }
315
316 closed_hash_set(const closed_hash_set& x) : data_m(x.data_m) {
317 header() = 0;
318 allocate(x.get_allocator(), x.size());
319 insert(x.begin(), x.end());
320 }
322 swap(x, *this);
323 return *this;
324 }
325
327 return header() ? header()->allocator() : allocator_type();
328 }
329
330 closed_hash_set(closed_hash_set&& x) noexcept : data_m(x.data_m) { x.header() = 0; }
331
332 // size and capacity
333
334 size_type size() const { return header() ? header()->size() : 0; }
335 size_type max_size() const { return size_type(-1) / sizeof(node_t); }
336 bool empty() const { return size() == 0; }
337 size_type capacity() const { return header() ? capacity_() : 0; }
338
340 if (n <= capacity())
341 return;
342
343 if (!header())
344 allocate(allocator_type(), n);
345 else {
347 tmp.move_insert(begin(), end());
348 swap(*this, tmp);
349 }
350 }
351
352 key_transform key_function() const { return data_m.second().second().first(); }
353 hasher hash_function() const { return data_m.first(); }
354 key_equal key_eq() const { return data_m.second().first(); }
355
356 iterator begin() { return iterator(header() ? header()->used_tail().next() : 0); }
357 iterator end() { return iterator(header() ? header()->used_tail().address() : 0); }
358
359 const_iterator begin() const { return iterator(header() ? header()->used_tail().next() : 0); }
361 return iterator(header() ? const_cast<node_t*>(header()->used_tail().address()) : 0);
362 }
363
366
369
371 iterator next(boost::next(location));
372 iterator result = next;
373
374 if ((location.state() == std::size_t(state_home)) && (next != end()) &&
375 (next.state() == std::size_t(state_misplaced))) {
376 swap(*next, *location);
377 result = location;
378 location = next;
379 }
380
381 unsafe::skip_node(location);
382 erase_raw(location);
383
384 --header()->size();
385
386 return result;
387 }
388
389 std::size_t erase(const key_type& key) {
390 iterator node(find(key));
391 if (node == end())
392 return 0;
393 erase(node);
394 return 1;
395 }
396
397 void clear() {
398 for (iterator first(begin()), last(end()); first != last; first = erase(first))
399 ;
400 }
401
402 const_iterator find(const key_type& key) const { return find(key, hash_function()(key)); }
403
404 const_iterator find(const key_type& key, std::size_t hash) const {
405 return adobe::remove_const(*this).find(key, hash);
406 }
407
408 iterator find(const key_type& key) { return find(key, hash_function()(key)); }
409
410 iterator find(const key_type& key, std::size_t hash) {
411 if (!header())
412 return iterator(0);
413
414 iterator node = bucket_(hash);
415 iterator last = end_();
416
417 if (node.state() != std::size_t(state_home))
418 return last;
419
420 return find(node, last, key);
421 }
422
423 std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
424 return equal_range(key, hash_function()(key));
425 }
426
427 std::pair<const_iterator, const_iterator> equal_range(const key_type& key, size_t hash) const {
428 const_iterator result = find(key, hash);
429 if (result == end())
430 return std::make_pair(result, result);
431 return std::make_pair(result, boost::next(result));
432 }
433
434
435 std::pair<iterator, iterator> equal_range(const key_type& key) {
436 return equal_range(key, hash_function()(key));
437 }
438
439 std::pair<iterator, iterator> equal_range(const key_type& key, std::size_t hash) {
440 iterator result = find(key, hash);
441 if (result == end())
442 return std::make_pair(result, result);
443 return std::make_pair(result, boost::next(result));
444 }
445
446
447 std::size_t count(const key_type& key) const { return count(key, hash_function()(key)); }
448 std::size_t count(const key_type& key, std::size_t hash) const {
449 return std::size_t(find(key, hash) != end());
450 }
451
452 template <typename I> // I models InputIterator
453 void insert(I first, I last) {
454 while (first != last) {
455 insert(*first);
456 ++first;
457 }
458 }
459
460 template <typename I> // I models ForwardIterator
461 void move_insert(I first, I last) {
462 while (first != last) {
463 insert(std::move(*first));
464 ++first;
465 }
466 }
467
468 std::pair<iterator, bool> insert(value_type x) {
469 return insert(x, hash_function()(key_function()(x)));
470 }
471
472 /*
473 NOTE (sparent): If there is not enough space for one element we will reserve the space
474 prior to attempting the insert even if the item is already in the hash table. Without
475 recalculating the bucket (a potentially expensive operation) there is no other solution.
476 */
477
478 std::pair<iterator, bool> insert(value_type x, std::size_t hash) {
479 if (capacity() == size())
480 reserve(size() ? 2 * size() : 3);
481
482 iterator node = bucket_(hash);
483
484 switch (node.state()) {
485 case state_home: {
486 iterator found = find(node, end_(), key_function()(x));
487 if (found != end()) {
488 return std::make_pair(found, false);
489 }
490
491 iterator free(begin_free());
492 insert_raw(free, std::move(x), state_misplaced);
493 unsafe::splice_node_range(node, free, free);
494 node = free;
495 } break;
496 case state_misplaced: {
497 iterator free(begin_free());
498 insert_raw(free, std::move(*node), state_misplaced);
499
500 unsafe::set_next(boost::prior(node), free);
501 unsafe::set_next(free, boost::next(node));
502
503 erase_raw(node);
504 }
505 // fall through
506 default: // state_free
507 {
508 insert_raw(node, std::move(x), state_home);
509 unsafe::splice_node_range(end(), node, node);
510 }
511 }
512 header()->size() += 1;
513 return std::make_pair(node, true);
514 }
515
516 iterator insert(iterator, value_type x) { return insert(std::move(x)).first; }
517
519 if (header()) {
520 for (iterator first(begin()), last(end()); first != last; ++first)
521 destroy(&*first);
522 raw_allocator alloc(get_allocator());
523 alloc.deallocate(reinterpret_cast<char*>(header()), 0);
524 }
525 }
526
527 friend void swap(closed_hash_set& x, closed_hash_set& y) { std::swap(x.data_m, y.data_m); }
528
529 friend bool operator==(const closed_hash_set& x, const closed_hash_set& y) {
530 if (x.size() != y.size())
531 return false;
532 for (const_iterator first(x.begin()), last(x.end()); first != last; ++first) {
533 const_iterator iter(y.find(y.key_function()(*first)));
534 if (iter == y.end() || !(*first == *iter))
535 return false;
536 }
537 return true;
538 }
539
540private:
541 typedef typename allocator_type::template rebind<char>::other raw_allocator;
542
543
544 void allocate(const allocator_type& a, size_type n) {
545 // table of primes such that p[n + 1] = next_prime(2 * p[n])
546
547 static const std::size_t prime_table[] = {
548 3UL, 7UL, 17UL, 37UL, 79UL, 163UL,
549 331UL, 673UL, 1361UL, 2729UL, 5471UL, 10949UL,
550 21911UL, 43853UL, 87719UL, 175447UL, 350899UL, 701819UL,
551 1403641UL, 2807303UL, 5614657UL, 11229331UL, 22458671UL, 44917381UL,
552 89834777UL, 179669557UL, 359339171UL, 718678369UL, 1437356741UL, 2874713497UL,
553 ULONG_MAX};
554
555 assert(!header() && "WARNING (sparent@adobe.com) : About to write over allocated header.");
556
557 if (n == 0 && a == allocator_type())
558 return;
559
560 n = *adobe::lower_bound(prime_table, n);
561
562 raw_allocator alloc(a);
563
564 header() = reinterpret_cast<header_t*>(
565 alloc.allocate(sizeof(header_t) - sizeof(node_t) + sizeof(node_t) * n));
566 header()->capacity() = n;
567 header()->size() = 0;
568 adobe::construct_at(&header()->free_tail());
569 adobe::construct_at(&header()->used_tail());
570 adobe::construct_at(&header()->allocator(), a);
571
572 node_t* prior = header()->free_tail().address();
573 for (node_ptr first(&header()->storage_m[0]), last(&header()->storage_m[0] + n);
574 first != last; ++first) {
575 set_next_raw(*prior, *first);
576 prior = first;
577 // first->set_state(state_free);
578 }
579 set_next_raw(*prior, header()->free_tail());
580 }
581
582
583 // precondition: header() != NULL
584 iterator end_() { return iterator(header()->used_tail().address()); }
585
586 // precondition: header() != NULL
587 size_type capacity_() const { return header()->capacity(); }
588
589 // precondition: header() != NULL
590 iterator bucket_(std::size_t hash) {
591 return iterator(&header()->storage_m[0] + hash % capacity_());
592 }
593
594 // preconditino: [f, l) is not empty
595 iterator find(iterator f, iterator l, const key_type& key) {
596 do {
597 if (key_eq()(key, key_function()(*f)))
598 return f;
599 ++f;
600 } while ((f != l) && (f.state() != std::size_t(state_home)));
601
602 return l;
603 }
604
605 // location points to a free node
606 static void insert_raw(iterator location, value_type x, std::size_t state) {
607 adobe::construct_at<value_type>(&*location, std::move(x));
608 location.set_state(state);
609 unsafe::skip_node(location);
610 }
611
612 // location points to a used but detatched node
613 void erase_raw(iterator location) {
614 destroy(&*location);
615 location.set_state(state_free);
616 unsafe::splice_node_range(end_free(), location, location);
617 }
618
619 iterator begin_free() { return iterator(header() ? header()->free_tail().next() : 0); }
620 iterator end_free() { return iterator(header() ? header()->free_tail().address() : 0); }
621};
622
623/**************************************************************************************************/
624
640
641template <typename Key, typename T, typename Hash, typename Pred, typename A>
642class closed_hash_map : public closed_hash_set<std::pair<Key, T>, get_element<0>, Hash, Pred, A> {
643
644 using set_type = closed_hash_set<std::pair<Key, T>, get_element<0>, Hash, Pred, A>;
645
646public:
647 typedef T mapped_type;
648
650
651 template <typename I> // I models InputIterator
652 closed_hash_map(I f, I l) : set_type(f, l) {}
653
654 closed_hash_map(std::initializer_list<typename set_type::value_type> init) : set_type(init) {}
655
656 closed_hash_map(const closed_hash_map& x) : set_type(x) {}
657 closed_hash_map(closed_hash_map&& x) noexcept : set_type(std::move(x)) {}
659 swap(x, *this);
660 return *this;
661 }
662
664 swap(static_cast<set_type&>(x), static_cast<set_type&>(y));
665 }
666
667
668 friend bool operator==(const closed_hash_map& x, const closed_hash_map& y) {
669 return static_cast<const set_type&>(x) == static_cast<const set_type&>(y);
670 }
671
672 /*
673 NOTE (sparent) : Can't use boost::equality_comparable without introducing extra base
674 class
675 overhead.
676 */
677
678 friend bool operator!=(const closed_hash_map& x, const closed_hash_map& y) { return !(x == y); }
679
680#ifndef ADOBE_CLOSED_HASH_MAP_INDEX
681#define ADOBE_CLOSED_HASH_MAP_INDEX 1
682#endif
683
684#if ADOBE_CLOSED_HASH_MAP_INDEX
685
686 mapped_type& operator[](const Key& x) {
687 typename set_type::iterator i = this->find(x);
688 if (i == this->end()) {
689 return this->insert(std::make_pair(x, mapped_type())).first->second;
690 }
691 return i->second;
692 }
693
694#endif
695};
696
697/**************************************************************************************************/
698
699static_assert(sizeof(closed_hash_set<int>) == sizeof(void*));
700
701
702#ifndef ADOBE_NO_DOCUMENTATION
703
704} // namespace version_1
705
706#endif
707
708/**************************************************************************************************/
709
710} // namespace adobe
711
712/**************************************************************************************************/
713
714#endif
715
716/**************************************************************************************************/
A hash based associative container.
closed_hash_map(const closed_hash_map &x)
closed_hash_map & operator=(closed_hash_map x)
friend bool operator!=(const closed_hash_map &x, const closed_hash_map &y)
closed_hash_map(std::initializer_list< typename set_type::value_type > init)
closed_hash_map(closed_hash_map &&x) noexcept
friend bool operator==(const closed_hash_map &x, const closed_hash_map &y)
friend void swap(closed_hash_map &x, closed_hash_map &y)
A hash based associative container.
const_reverse_iterator rend() const
iterator erase(iterator location)
void move_insert(I first, I last)
iterator find(const key_type &key, std::size_t hash)
closed_hash_set(size_type n, const hasher &hf, const key_equal &eq=key_equal(), const key_transform &kf=key_transform(), const allocator_type &a=allocator_type())
std::pair< iterator, bool > insert(value_type x)
std::reverse_iterator< const_iterator > const_reverse_iterator
std::pair< const_iterator, const_iterator > equal_range(const key_type &key) const
std::size_t erase(const key_type &key)
std::pair< iterator, bool > insert(value_type x, std::size_t hash)
std::pair< const_iterator, const_iterator > equal_range(const key_type &key, size_t hash) const
allocator_type get_allocator() const
const_iterator find(const key_type &key, std::size_t hash) const
std::size_t count(const key_type &key, std::size_t hash) const
closed_hash_set(closed_hash_set &&x) noexcept
friend bool operator==(const closed_hash_set &x, const closed_hash_set &y)
std::reverse_iterator< iterator > reverse_iterator
std::size_t count(const key_type &key) const
implementation::closed_hash_iterator< closed_hash_set, const value_type > const_iterator
iterator find(const key_type &key)
iterator insert(iterator, value_type x)
std::pair< iterator, iterator > equal_range(const key_type &key, std::size_t hash)
std::remove_reference_t< adobe::invoke_result_t< KeyTransform, T > > key_type
implementation::closed_hash_iterator< closed_hash_set, value_type > iterator
closed_hash_set & operator=(closed_hash_set x)
closed_hash_set(std::initializer_list< value_type > init)
closed_hash_set(const closed_hash_set &x)
std::pair< iterator, iterator > equal_range(const key_type &key)
const_reverse_iterator rbegin() const
closed_hash_set(I f, I l, size_type n, const hasher &hf=hasher(), const key_equal &eq=key_equal(), const key_transform &kf=key_transform(), const allocator_type &a=allocator_type())
void skip_node(I location)
Definition set_next.hpp:70
void splice_node_range(I location, I first, I last)
Definition set_next.hpp:58
void set_next(I x, I y)
Definition set_next.hpp:44
T & remove_const(const T &x)
boost::range_iterator< InputRange >::type find(InputRange &range, const T &value)
find implementation
Definition find.hpp:144
boost::range_size< Selection >::type size(const Selection &x)
I lower_bound(I f, I l, const T &x)
void destroy(T *p)
Definition memory.hpp:341
T * construct_at(T *p, Args &&... args)
Definition memory.hpp:350
void swap(adobe::extents_t::slice_t &x, adobe::extents_t::slice_t &y) BOOST_NOEXCEPT
Definition extents.hpp:120
boost::compressed_pair< allocator_type, node_base_t > alloc_free_tail_m