hat-trie
htrie_hash.h
Go to the documentation of this file.
1 /**
2  * MIT License
3  *
4  * Copyright (c) 2017 Tessil
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #ifndef TSL_HTRIE_HASH_H
25 #define TSL_HTRIE_HASH_H
26 
27 #include <algorithm>
28 #include <array>
29 #include <cmath>
30 #include <cstddef>
31 #include <cstdint>
32 #include <cstring>
33 #include <iterator>
34 #include <limits>
35 #include <memory>
36 #include <stdexcept>
37 #include <string>
38 #include <type_traits>
39 #include <utility>
40 #include <vector>
41 #include "array-hash/array_map.h"
42 #include "array-hash/array_set.h"
43 
44 
45 /*
46  * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433),
47  * check also __cplusplus version.
48  */
49 #ifdef __has_include
50 # if __has_include(<string_view>) && __cplusplus >= 201703L
51 # define TSL_HT_HAS_STRING_VIEW
52 # endif
53 #endif
54 
55 
56 #ifdef TSL_HT_HAS_STRING_VIEW
57 # include <string_view>
58 #endif
59 
60 
61 #ifdef TSL_DEBUG
62 # define tsl_ht_assert(expr) assert(expr)
63 #else
64 # define tsl_ht_assert(expr) (static_cast<void>(0))
65 #endif
66 
67 
68 namespace tsl {
69 
70 namespace detail_htrie_hash {
71 
72 template<typename T, typename = void>
73 struct is_iterator: std::false_type {
74 };
75 
76 template<typename T>
77 struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::iterator_category, void>::value>::type>: std::true_type {
78 };
79 
80 template<typename T, typename... U>
81 struct is_related: std::false_type {};
82 
83 template<typename T, typename U>
84 struct is_related<T, U>: std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type,
85  typename std::remove_cv<typename std::remove_reference<U>::type>::type> {};
86 
87 template<typename T, typename U>
88 static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") {
89  T ret = static_cast<T>(value);
90  if(static_cast<U>(ret) != value) {
91  throw std::runtime_error(error_message);
92  }
93 
94  const bool is_same_signedness = (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
95  (std::is_signed<T>::value && std::is_signed<U>::value);
96  if(!is_same_signedness && (ret < T{}) != (value < U{})) {
97  throw std::runtime_error(error_message);
98  }
99 
100  return ret;
101 }
102 
103 
104 
105 template<class T>
106 struct value_node {
107  /*
108  * Avoid confict with copy constructor 'value_node(const value_node&)'. If we call the copy constructor
109  * with a mutable reference 'value_node(value_node&)', we don't want the forward constructor to be called.
110  */
111  template<class... Args, typename std::enable_if<!is_related<value_node, Args...>::value>::type* = nullptr>
112  value_node(Args&&... args): m_value(std::forward<Args>(args)...) {
113  }
114 
115  T m_value;
116 };
117 
118 template<>
119 struct value_node<void> {
120 };
121 
122 
123 /**
124  * T should be void if there is no value associated to a key (in a set for example).
125  */
126 template<class CharT,
127  class T,
128  class Hash,
129  class KeySizeT>
130 class htrie_hash {
131 private:
132  template<typename U>
133  using has_value = typename std::integral_constant<bool, !std::is_same<U, void>::value>;
134 
135  static_assert(std::is_same<CharT, char>::value, "char is the only supported CharT type for now.");
136 
137  static const std::size_t ALPHABET_SIZE =
138  std::numeric_limits<typename std::make_unsigned<CharT>::type>::max() + 1;
139 
140 
141 public:
142  template<bool IsConst, bool IsPrefixIterator>
144 
145 
146  using char_type = CharT;
147  using key_size_type = KeySizeT;
148  using size_type = std::size_t;
149  using hasher = Hash;
150  using iterator = htrie_hash_iterator<false, false>;
151  using const_iterator = htrie_hash_iterator<true, false>;
152  using prefix_iterator = htrie_hash_iterator<false, true>;
153  using const_prefix_iterator = htrie_hash_iterator<true, true>;
154 
155 
156 private:
157  using array_hash_type =
158  typename std::conditional<
159  has_value<T>::value,
160  tsl::array_map<CharT, T, Hash, tsl::ah::str_equal<CharT>, false,
161  KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>,
162  tsl::array_set<CharT, Hash, tsl::ah::str_equal<CharT>, false,
163  KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>>::type;
164 
165 
166 private:
167  /*
168  * The tree is mainly composed of two nodes types: trie_node and hash_node which both have anode as base class.
169  * Each child is either a hash_node or a trie_node.
170  *
171  * A hash_node is always a leaf node, it doesn't have any child.
172  *
173  * Example:
174  * | ... | a |.. ..................... | f | ... | trie_node_1
175  * \ \
176  * hash_node_1 |array_hash = {"dd"}| |...| a | ... | trie_node_2
177  * /
178  * |array_hash = {"ble", "bric", "lse"}| hash_node_2
179  *
180  *
181  * Each trie_node may also have a value node, which contains a value T, if the trie_node marks
182  * the end of a string value.
183  *
184  * A trie node should at least have one child or a value node. There can't be a trie node without
185  * any child and no value node.
186  */
187 
188  using value_node = tsl::detail_htrie_hash::value_node<T>;
189 
190 
191  class trie_node;
192  class hash_node;
193 
194  // TODO better encapsulate operations modifying the tree.
195  class anode {
196  friend class trie_node;
197 
198  public:
199  /*
200  * TODO Avoid the virtual to economise 8 bytes. We could use a custom deleter in the std::unique_ptr<anode>
201  * we use (as we know if an anode is a trie_node or hash_node).
202  */
203  virtual ~anode() = default;
204 
205  bool is_trie_node() const noexcept {
206  return m_node_type == node_type::TRIE_NODE;
207  }
208 
209  bool is_hash_node() const noexcept {
210  return m_node_type == node_type::HASH_NODE;
211  }
212 
213  trie_node& as_trie_node() noexcept {
214  tsl_ht_assert(is_trie_node());
215  return static_cast<trie_node&>(*this);
216  }
217 
218  hash_node& as_hash_node() noexcept {
219  tsl_ht_assert(is_hash_node());
220  return static_cast<hash_node&>(*this);
221  }
222 
223  const trie_node& as_trie_node() const noexcept {
224  tsl_ht_assert(is_trie_node());
225  return static_cast<const trie_node&>(*this);
226  }
227 
228  const hash_node& as_hash_node() const noexcept {
229  tsl_ht_assert(is_hash_node());
230  return static_cast<const hash_node&>(*this);
231  }
232 
233  /**
234  * @see m_child_of_char
235  */
236  CharT child_of_char() const noexcept {
237  tsl_ht_assert(parent() != nullptr);
238  return m_child_of_char;
239  }
240 
241  /**
242  * Return nullptr if none.
243  */
244  trie_node* parent() noexcept {
245  return m_parent_node;
246  }
247 
248  const trie_node* parent() const noexcept {
249  return m_parent_node;
250  }
251 
252  protected:
253  enum class node_type: unsigned char {
254  HASH_NODE,
255  TRIE_NODE
256  };
257 
258  anode(node_type node_type_): m_node_type(node_type_), m_child_of_char(0),
259  m_parent_node(nullptr)
260  {
261  }
262 
263  anode(node_type node_type_, CharT child_of_char): m_node_type(node_type_),
264  m_child_of_char(child_of_char),
265  m_parent_node(nullptr)
266  {
267  }
268 
269 
270  protected:
271  node_type m_node_type;
272 
273  /**
274  * If the node has a parent, then it's a descendant of some char.
275  *
276  * Example:
277  * | ... | a | b | ... | trie_node_1
278  * \
279  * |...| a | ... | trie_node_2
280  * /
281  * |array_hash| hash_node_1
282  *
283  * trie_node_2 is a child of trie_node_1 through 'b', it will have 'b' as m_child_of_char.
284  * hash_node_1 is a child of trie_node_2 through 'a', it will have 'a' as m_child_of_char.
285  *
286  * trie_node_1 has no parent, its m_child_of_char is undeterminated.
287  */
288  CharT m_child_of_char;
289  trie_node* m_parent_node;
290  };
291 
292  // Give the position in trie_node::m_children corresponding to the character c
293  static std::size_t as_position(CharT c) noexcept {
294  return static_cast<std::size_t>(static_cast<typename std::make_unsigned<CharT>::type>(c));
295  }
296 
297  class trie_node: public anode {
298  public:
299  trie_node(): anode(anode::node_type::TRIE_NODE),
300  m_value_node(nullptr), m_children()
301  {
302  }
303 
304  trie_node(const trie_node& other): anode(anode::node_type::TRIE_NODE, other.m_child_of_char),
305  m_value_node(nullptr), m_children()
306  {
307  if(other.m_value_node != nullptr) {
308  m_value_node.reset(new value_node(*other.m_value_node));
309  }
310 
311  // TODO avoid recursion
312  for(std::size_t ichild = 0; ichild < other.m_children.size(); ichild++) {
313  if(other.m_children[ichild] != nullptr) {
314  if(other.m_children[ichild]->is_hash_node()) {
315  m_children[ichild].reset(new hash_node(other.m_children[ichild]->as_hash_node()));
316  }
317  else {
318  m_children[ichild].reset(new trie_node(other.m_children[ichild]->as_trie_node()));
319  }
320 
321  m_children[ichild]->m_parent_node = this;
322  }
323  }
324  }
325 
326  trie_node(trie_node&& other) = delete;
327  trie_node& operator=(const trie_node& other) = delete;
328  trie_node& operator=(trie_node&& other) = delete;
329 
330  /**
331  * Return nullptr if none.
332  */
333  anode* first_child() noexcept {
334  return const_cast<anode*>(static_cast<const trie_node*>(this)->first_child());
335  }
336 
337  const anode* first_child() const noexcept {
338  for(std::size_t ichild = 0; ichild < m_children.size(); ichild++) {
339  if(m_children[ichild] != nullptr) {
340  return m_children[ichild].get();
341  }
342  }
343 
344  return nullptr;
345  }
346 
347 
348  /**
349  * Get the next_child that come after current_child. Return nullptr if no next child.
350  */
351  anode* next_child(const anode& current_child) noexcept {
352  return const_cast<anode*>(static_cast<const trie_node*>(this)->next_child(current_child));
353  }
354 
355  const anode* next_child(const anode& current_child) const noexcept {
356  tsl_ht_assert(current_child.parent() == this);
357 
358  for(std::size_t ichild = as_position(current_child.child_of_char()) + 1;
359  ichild < m_children.size();
360  ichild++)
361  {
362  if(m_children[ichild] != nullptr) {
363  return m_children[ichild].get();
364  }
365  }
366 
367  return nullptr;
368  }
369 
370 
371  /**
372  * Return the first left-descendant trie node with an m_value_node. If none return the most left trie node.
373  */
374  trie_node& most_left_descendant_value_trie_node() noexcept {
375  return const_cast<trie_node&>(static_cast<const trie_node*>(this)->most_left_descendant_value_trie_node());
376  }
377 
378  const trie_node& most_left_descendant_value_trie_node() const noexcept {
379  const trie_node* current_node = this;
380  while(true) {
381  if(current_node->m_value_node != nullptr) {
382  return *current_node;
383  }
384 
385  const anode* first_child = current_node->first_child();
386  tsl_ht_assert(first_child != nullptr); // a trie_node must either have a value_node or at least one child.
387  if(first_child->is_hash_node()) {
388  return *current_node;
389  }
390 
391  current_node = &first_child->as_trie_node();
392  }
393  }
394 
395 
396 
397  size_type nb_children() const noexcept {
398  return std::count_if(m_children.cbegin(), m_children.cend(),
399  [](const std::unique_ptr<anode>& n) { return n != nullptr; });
400  }
401 
402  bool empty() const noexcept {
403  return std::all_of(m_children.cbegin(), m_children.cend(),
404  [](const std::unique_ptr<anode>& n) { return n == nullptr; });
405  }
406 
407  std::unique_ptr<anode>& child(CharT for_char) noexcept {
408  return m_children[as_position(for_char)];
409  }
410 
411  const std::unique_ptr<anode>& child(CharT for_char) const noexcept {
412  return m_children[as_position(for_char)];
413  }
414 
415  typename std::array<std::unique_ptr<anode>, ALPHABET_SIZE>::iterator begin() noexcept {
416  return m_children.begin();
417  }
418 
419  typename std::array<std::unique_ptr<anode>, ALPHABET_SIZE>::iterator end() noexcept {
420  return m_children.end();
421  }
422 
423  void set_child(CharT for_char, std::unique_ptr<anode> child) noexcept {
424  if(child != nullptr) {
425  child->m_child_of_char = for_char;
426  child->m_parent_node = this;
427  }
428 
429  m_children[as_position(for_char)] = std::move(child);
430  }
431 
432  std::unique_ptr<value_node>& val_node() noexcept {
433  return m_value_node;
434  }
435 
436  const std::unique_ptr<value_node>& val_node() const noexcept {
437  return m_value_node;
438  }
439 
440  private:
441  // TODO Avoid storing a value_node when has_value<T>::value is false
442  std::unique_ptr<value_node> m_value_node;
443 
444  /**
445  * Each character CharT corresponds to one position in the array. To convert a character
446  * to a position use the as_position method.
447  *
448  * TODO Try to reduce the size of m_children with a hash map, linear/binary search on array, ...
449  * TODO Store number of non-null values in m_children. Check if we can store this value in the alignment
450  * space as we don't want the node to get bigger (empty() and nb_children() are rarely used so it is
451  * not an important variable).
452  */
453  std::array<std::unique_ptr<anode>, ALPHABET_SIZE> m_children;
454  };
455 
456 
457  class hash_node: public anode {
458  public:
459  hash_node(const Hash& hash, float max_load_factor):
460  hash_node(HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT, hash, max_load_factor)
461  {
462  }
463 
464  hash_node(size_type bucket_count, const Hash& hash, float max_load_factor):
465  anode(anode::node_type::HASH_NODE), m_array_hash(bucket_count, hash)
466  {
467  m_array_hash.max_load_factor(max_load_factor);
468  }
469 
470  hash_node(array_hash_type&& array_hash) noexcept(std::is_nothrow_move_constructible<array_hash_type>::value):
471  anode(anode::node_type::HASH_NODE), m_array_hash(std::move(array_hash))
472  {
473  }
474 
475  hash_node(const hash_node& other) = default;
476 
477  hash_node(hash_node&& other) = delete;
478  hash_node& operator=(const hash_node& other) = delete;
479  hash_node& operator=(hash_node&& other) = delete;
480 
481 
482  array_hash_type& array_hash() noexcept {
483  return m_array_hash;
484  }
485 
486  const array_hash_type& array_hash() const noexcept {
487  return m_array_hash;
488  }
489 
490  private:
491  array_hash_type m_array_hash;
492  };
493 
494 
495 
496 public:
497  template<bool IsConst, bool IsPrefixIterator>
498  class htrie_hash_iterator {
499  friend class htrie_hash;
500 
501  private:
502  using anode_type = typename std::conditional<IsConst, const anode, anode>::type;
503  using trie_node_type = typename std::conditional<IsConst, const trie_node, trie_node>::type;
504  using hash_node_type = typename std::conditional<IsConst, const hash_node, hash_node>::type;
505 
506  using array_hash_iterator_type =
507  typename std::conditional<IsConst,
508  typename array_hash_type::const_iterator,
509  typename array_hash_type::iterator>::type;
510 
511  public:
512  using iterator_category = std::forward_iterator_tag;
513  using value_type = typename std::conditional<has_value<T>::value, T, void>::type;
514  using difference_type = std::ptrdiff_t;
515  using reference = typename std::conditional<
516  has_value<T>::value,
517  typename std::conditional<IsConst,
518  typename std::add_lvalue_reference<const T>::type,
519  typename std::add_lvalue_reference<T>::type>::type,
520  void>::type;
521  using pointer = typename std::conditional<
522  has_value<T>::value,
523  typename std::conditional<IsConst, const T*, T*>::type,
524  void>::type;
525 
526  private:
527  /**
528  * Start reading from start_hash_node->array_hash().begin().
529  */
530  htrie_hash_iterator(hash_node_type& start_hash_node) noexcept:
531  htrie_hash_iterator(start_hash_node, start_hash_node.array_hash().begin())
532  {
533  }
534 
535  /**
536  * Start reading from iterator begin in start_hash_node->array_hash().
537  */
538  htrie_hash_iterator(hash_node_type& start_hash_node, array_hash_iterator_type begin) noexcept:
539  m_current_trie_node(start_hash_node.parent()), m_current_hash_node(&start_hash_node),
540  m_array_hash_iterator(begin),
541  m_array_hash_end_iterator(start_hash_node.array_hash().end()),
542  m_read_trie_node_value(false)
543  {
544  tsl_ht_assert(!m_current_hash_node->array_hash().empty());
545  }
546 
547  /**
548  * Start reading from the value in start_trie_node. start_trie_node->val_node() should be non-null.
549  */
550  htrie_hash_iterator(trie_node_type& start_trie_node) noexcept:
551  m_current_trie_node(&start_trie_node), m_current_hash_node(nullptr),
552  m_read_trie_node_value(true)
553  {
554  tsl_ht_assert(m_current_trie_node->val_node() != nullptr);
555  }
556 
557  template<bool P = IsPrefixIterator, typename std::enable_if<!P>::type* = nullptr>
558  htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode,
559  array_hash_iterator_type begin, array_hash_iterator_type end,
560  bool read_trie_node_value) noexcept:
561  m_current_trie_node(tnode), m_current_hash_node(hnode),
562  m_array_hash_iterator(begin), m_array_hash_end_iterator(end),
563  m_read_trie_node_value(read_trie_node_value)
564  {
565  }
566 
567  template<bool P = IsPrefixIterator, typename std::enable_if<P>::type* = nullptr>
568  htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode,
569  array_hash_iterator_type begin, array_hash_iterator_type end,
570  bool read_trie_node_value, std::basic_string<CharT> prefix_filter) noexcept:
571  m_current_trie_node(tnode), m_current_hash_node(hnode),
572  m_array_hash_iterator(begin), m_array_hash_end_iterator(end),
573  m_read_trie_node_value(read_trie_node_value), m_prefix_filter(std::move(prefix_filter))
574  {
575  }
576 
577  public:
578  htrie_hash_iterator() noexcept {
579  }
580 
581  template<bool P = IsPrefixIterator, typename std::enable_if<!P>::type* = nullptr>
582  htrie_hash_iterator(const htrie_hash_iterator<false, false>& other) noexcept:
583  m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node),
584  m_array_hash_iterator(other.m_array_hash_iterator),
585  m_array_hash_end_iterator(other.m_array_hash_end_iterator),
586  m_read_trie_node_value(other.m_read_trie_node_value)
587  {
588  }
589 
590  template<bool P = IsPrefixIterator, typename std::enable_if<P>::type* = nullptr>
591  htrie_hash_iterator(const htrie_hash_iterator<false, true>& other) noexcept:
592  m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node),
593  m_array_hash_iterator(other.m_array_hash_iterator),
594  m_array_hash_end_iterator(other.m_array_hash_end_iterator),
595  m_read_trie_node_value(other.m_read_trie_node_value), m_prefix_filter(other.m_prefix_filter)
596  {
597  }
598 
599  void key(std::basic_string<CharT>& key_buffer_out) const {
600  key_buffer_out.clear();
601 
602  trie_node_type* tnode = m_current_trie_node;
603  while(tnode != nullptr && tnode->parent() != nullptr) {
604  key_buffer_out.push_back(tnode->child_of_char());
605  tnode = tnode->parent();
606  }
607 
608  std::reverse(key_buffer_out.begin(), key_buffer_out.end());
609 
610  if(!m_read_trie_node_value) {
611  tsl_ht_assert(m_current_hash_node != nullptr);
612  if(m_current_hash_node->parent() != nullptr) {
613  key_buffer_out.push_back(m_current_hash_node->child_of_char());
614  }
615 
616  key_buffer_out.append(m_array_hash_iterator.key(), m_array_hash_iterator.key_size());
617  }
618  }
619 
620  std::basic_string<CharT> key() const {
621  std::basic_string<CharT> key_buffer;
622  key(key_buffer);
623 
624  return key_buffer;
625  }
626 
627  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
628  reference value() const {
629  if(this->m_read_trie_node_value) {
630  tsl_ht_assert(this->m_current_trie_node != nullptr);
631  tsl_ht_assert(this->m_current_trie_node->val_node() != nullptr);
632 
633  return this->m_current_trie_node->val_node()->m_value;
634  }
635  else {
636  return this->m_array_hash_iterator.value();
637  }
638  }
639 
640  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
641  reference operator*() const {
642  return value();
643  }
644 
645  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
646  pointer operator->() const {
647  return std::addressof(value());
648  }
649 
651  if(m_read_trie_node_value) {
652  tsl_ht_assert(m_current_trie_node != nullptr);
653 
654  m_read_trie_node_value = false;
655 
656  anode_type* child = m_current_trie_node->first_child();
657  if(child != nullptr) {
658  set_most_left_descendant_as_next_node(*child);
659  }
660  else if(m_current_trie_node->parent() != nullptr) {
661  trie_node_type* current_node_child = m_current_trie_node;
662  m_current_trie_node = m_current_trie_node->parent();
663 
664  set_next_node_ascending(*current_node_child);
665  }
666  else {
667  set_as_end_iterator();
668  }
669  }
670  else {
671  ++m_array_hash_iterator;
672  if(m_array_hash_iterator != m_array_hash_end_iterator) {
673  filter_prefix();
674  }
675  // End of the road, set the iterator as an end node.
676  else if(m_current_trie_node == nullptr) {
677  set_as_end_iterator();
678  }
679  else {
680  tsl_ht_assert(m_current_hash_node != nullptr);
681  set_next_node_ascending(*m_current_hash_node);
682  }
683  }
684 
685 
686  return *this;
687  }
688 
690  htrie_hash_iterator tmp(*this);
691  ++*this;
692 
693  return tmp;
694  }
695 
696  friend bool operator==(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) {
697  if(lhs.m_current_trie_node != rhs.m_current_trie_node ||
698  lhs.m_read_trie_node_value != rhs.m_read_trie_node_value)
699  {
700  return false;
701  }
702  else if(lhs.m_read_trie_node_value) {
703  return true;
704  }
705  else {
706  if(lhs.m_current_hash_node != rhs.m_current_hash_node) {
707  return false;
708  }
709  else if(lhs.m_current_hash_node == nullptr) {
710  return true;
711  }
712  else {
713  return lhs.m_array_hash_iterator == rhs.m_array_hash_iterator &&
714  lhs.m_array_hash_end_iterator == rhs.m_array_hash_end_iterator;
715  }
716  }
717  }
718 
719  friend bool operator!=(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) {
720  return !(lhs == rhs);
721  }
722 
723  private:
724  void hash_node_prefix(std::basic_string<CharT>& key_buffer_out) const {
725  tsl_ht_assert(!m_read_trie_node_value);
726  key_buffer_out.clear();
727 
728  trie_node_type* tnode = m_current_trie_node;
729  while(tnode != nullptr && tnode->parent() != nullptr) {
730  key_buffer_out.push_back(tnode->child_of_char());
731  tnode = tnode->parent();
732  }
733 
734  std::reverse(key_buffer_out.begin(), key_buffer_out.end());
735 
736  tsl_ht_assert(m_current_hash_node != nullptr);
737  if(m_current_hash_node->parent() != nullptr) {
738  key_buffer_out.push_back(m_current_hash_node->child_of_char());
739  }
740  }
741 
742  template<bool P = IsPrefixIterator, typename std::enable_if<!P>::type* = nullptr>
743  void filter_prefix() {
744  }
745 
746  template<bool P = IsPrefixIterator, typename std::enable_if<P>::type* = nullptr>
747  void filter_prefix() {
748  tsl_ht_assert(m_array_hash_iterator != m_array_hash_end_iterator);
749  tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr);
750 
751  if(m_prefix_filter.empty()) {
752  return;
753  }
754 
755  while((m_prefix_filter.size() > m_array_hash_iterator.key_size() ||
756  m_prefix_filter.compare(0, m_prefix_filter.size(),
757  m_array_hash_iterator.key(), m_prefix_filter.size()) != 0))
758  {
759  ++m_array_hash_iterator;
760  if(m_array_hash_iterator == m_array_hash_end_iterator) {
761  if(m_current_trie_node == nullptr) {
762  set_as_end_iterator();
763  }
764  else {
765  tsl_ht_assert(m_current_hash_node != nullptr);
766  set_next_node_ascending(*m_current_hash_node);
767  }
768 
769  return;
770  }
771  }
772  }
773 
774  /**
775  * Go back up in the tree to get the current_trie_node_child sibling.
776  * If none, try to go back up more in the tree to check the siblings of the ancestors.
777  */
778  void set_next_node_ascending(anode_type& current_trie_node_child) {
779  tsl_ht_assert(m_current_trie_node != nullptr);
780  tsl_ht_assert(current_trie_node_child.parent() == m_current_trie_node);
781 
782  anode_type* next_node = m_current_trie_node->next_child(current_trie_node_child);
783  while(next_node == nullptr && m_current_trie_node->parent() != nullptr) {
784  anode_type* current_child = m_current_trie_node;
785  m_current_trie_node = m_current_trie_node->parent();
786  next_node = m_current_trie_node->next_child(*current_child);
787  }
788 
789  // End of the road, set the iterator as an end node.
790  if(next_node == nullptr) {
791  set_as_end_iterator();
792  }
793  else {
794  set_most_left_descendant_as_next_node(*next_node);
795  }
796  }
797 
798  void set_most_left_descendant_as_next_node(anode_type& search_start) {
799  if(search_start.is_hash_node()) {
800  set_current_hash_node(search_start.as_hash_node());
801  }
802  else {
803  m_current_trie_node = &search_start.as_trie_node().most_left_descendant_value_trie_node();
804  if(m_current_trie_node->val_node() != nullptr) {
805  m_read_trie_node_value = true;
806  }
807  else {
808  anode_type* first_child = m_current_trie_node->first_child();
809  // a trie_node must either have a value_node or at least one child.
810  tsl_ht_assert(first_child != nullptr);
811 
812  set_current_hash_node(first_child->as_hash_node());
813  }
814  }
815  }
816 
817  void set_current_hash_node(hash_node_type& hnode) {
818  tsl_ht_assert(!hnode.array_hash().empty());
819 
820  m_current_hash_node = &hnode;
821  m_array_hash_iterator = m_current_hash_node->array_hash().begin();
822  m_array_hash_end_iterator = m_current_hash_node->array_hash().end();
823  }
824 
825  void set_as_end_iterator() {
826  m_current_trie_node = nullptr;
827  m_current_hash_node = nullptr;
828  m_read_trie_node_value = false;
829  }
830 
831  void skip_hash_node() {
832  tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr);
833  if(m_current_trie_node == nullptr) {
834  set_as_end_iterator();
835  }
836  else {
837  tsl_ht_assert(m_current_hash_node != nullptr);
838  set_next_node_ascending(*m_current_hash_node);
839  }
840  }
841 
842  private:
843  trie_node_type* m_current_trie_node;
844  hash_node_type* m_current_hash_node;
845 
846  array_hash_iterator_type m_array_hash_iterator;
847  array_hash_iterator_type m_array_hash_end_iterator;
848 
849  bool m_read_trie_node_value;
850  // TODO can't have void if !IsPrefixIterator, use inheritance
851  typename std::conditional<IsPrefixIterator, std::basic_string<CharT>, bool>::type m_prefix_filter;
852  };
853 
854 
855 
856 public:
857  htrie_hash(const Hash& hash, float max_load_factor, size_type burst_threshold):
858  m_root(nullptr), m_nb_elements(0),
859  m_hash(hash), m_max_load_factor(max_load_factor)
860  {
861  this->burst_threshold(burst_threshold);
862  }
863 
864  htrie_hash(const htrie_hash& other): m_root(nullptr), m_nb_elements(other.m_nb_elements),
865  m_hash(other.m_hash), m_max_load_factor(other.m_max_load_factor),
866  m_burst_threshold(other.m_burst_threshold)
867  {
868  if(other.m_root != nullptr) {
869  if(other.m_root->is_hash_node()) {
870  m_root.reset(new hash_node(other.m_root->as_hash_node()));
871  }
872  else {
873  m_root.reset(new trie_node(other.m_root->as_trie_node()));
874  }
875  }
876  }
877 
879  : m_root(std::move(other.m_root)),
880  m_nb_elements(other.m_nb_elements),
881  m_hash(std::move(other.m_hash)),
882  m_max_load_factor(other.m_max_load_factor),
883  m_burst_threshold(other.m_burst_threshold)
884  {
885  other.clear();
886  }
887 
888  htrie_hash& operator=(const htrie_hash& other) {
889  if(&other != this) {
890  std::unique_ptr<anode> new_root = nullptr;
891  if(other.m_root != nullptr) {
892  if(other.m_root->is_hash_node()) {
893  new_root.reset(new hash_node(other.m_root->as_hash_node()));
894  }
895  else {
896  new_root.reset(new trie_node(other.m_root->as_trie_node()));
897  }
898  }
899 
900  m_hash = other.m_hash;
901  m_root = std::move(new_root);
902  m_nb_elements = other.m_nb_elements;
903  m_max_load_factor = other.m_max_load_factor;
904  m_burst_threshold = other.m_burst_threshold;
905  }
906 
907  return *this;
908  }
909 
911  other.swap(*this);
912  other.clear();
913 
914  return *this;
915  }
916 
917  /*
918  * Iterators
919  */
920  iterator begin() noexcept {
921  return mutable_iterator(cbegin());
922  }
923 
924  const_iterator begin() const noexcept {
925  return cbegin();
926  }
927 
928  const_iterator cbegin() const noexcept {
929  if(empty()) {
930  return cend();
931  }
932 
933  return cbegin<const_iterator>(*m_root);
934  }
935 
936  iterator end() noexcept {
937  iterator it;
938  it.set_as_end_iterator();
939 
940  return it;
941  }
942 
943  const_iterator end() const noexcept {
944  return cend();
945  }
946 
947  const_iterator cend() const noexcept {
948  const_iterator it;
949  it.set_as_end_iterator();
950 
951  return it;
952  }
953 
954 
955  /*
956  * Capacity
957  */
958  bool empty() const noexcept {
959  return m_nb_elements == 0;
960  }
961 
962  size_type size() const noexcept {
963  return m_nb_elements;
964  }
965 
966  size_type max_size() const noexcept {
967  return std::numeric_limits<size_type>::max();
968  }
969 
970  size_type max_key_size() const noexcept {
971  return array_hash_type::MAX_KEY_SIZE;
972  }
973 
974  void shrink_to_fit() {
975  auto first = begin();
976  auto last = end();
977 
978  while(first != last) {
979  if(first.m_read_trie_node_value) {
980  ++first;
981  }
982  else {
983  /*
984  * skrink_to_fit on array_hash will invalidate the iterators of array_hash.
985  * Save pointer to array_hash, skip the array_hash_node and then call
986  * shrink_to_fit on the saved pointer.
987  */
988  hash_node* hnode = first.m_current_hash_node;
989  first.skip_hash_node();
990 
991  tsl_ht_assert(hnode != nullptr);
992  hnode->array_hash().shrink_to_fit();
993  }
994  }
995  }
996 
997 
998  /*
999  * Modifiers
1000  */
1001  void clear() noexcept {
1002  m_root.reset(nullptr);
1003  m_nb_elements = 0;
1004  }
1005 
1006  template<class... ValueArgs>
1007  std::pair<iterator, bool> insert(const CharT* key, size_type key_size, ValueArgs&&... value_args) {
1008  if(key_size > max_key_size()) {
1009  throw std::length_error("Key is too long.");
1010  }
1011 
1012  if(m_root == nullptr) {
1013  m_root.reset(new hash_node(m_hash, m_max_load_factor));
1014  }
1015 
1016  return insert_impl(*m_root, key, key_size, std::forward<ValueArgs>(value_args)...);
1017  }
1018 
1019  iterator erase(const_iterator pos) {
1020  return erase(mutable_iterator(pos));
1021  }
1022 
1023  iterator erase(const_iterator first, const_iterator last) {
1024  // TODO Optimize, could avoid the call to std::distance
1025  const std::size_t nb_to_erase = std::size_t(std::distance(first, last));
1026  auto to_delete = mutable_iterator(first);
1027  for(std::size_t i = 0; i < nb_to_erase; i++) {
1028  to_delete = erase(to_delete);
1029  }
1030 
1031  return to_delete;
1032  }
1033 
1034  size_type erase(const CharT* key, size_type key_size) {
1035  auto it = find(key, key_size);
1036  if(it != end()) {
1037  erase(it);
1038  return 1;
1039  }
1040  else {
1041  return 0;
1042  }
1043 
1044  }
1045 
1046  size_type erase_prefix(const CharT* prefix, size_type prefix_size) {
1047  if(m_root == nullptr) {
1048  return 0;
1049  }
1050 
1051  anode* current_node = m_root.get();
1052  for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) {
1053  if(current_node->is_trie_node()) {
1054  trie_node* tnode = &current_node->as_trie_node();
1055 
1056  if(tnode->child(prefix[iprefix]) == nullptr) {
1057  return 0;
1058  }
1059  else {
1060  current_node = tnode->child(prefix[iprefix]).get();
1061  }
1062  }
1063  else {
1064  hash_node& hnode = current_node->as_hash_node();
1065  return erase_prefix_hash_node(hnode, prefix + iprefix, prefix_size - iprefix);
1066  }
1067  }
1068 
1069 
1070  if(current_node->is_trie_node()) {
1071  trie_node* parent = current_node->parent();
1072 
1073  if(parent != nullptr) {
1074  const size_type nb_erased = size_descendants(current_node->as_trie_node());
1075 
1076  parent->set_child(current_node->child_of_char(), nullptr);
1077  m_nb_elements -= nb_erased;
1078 
1079  if(parent->empty()) {
1080  clear_empty_nodes(*parent);
1081  }
1082 
1083  return nb_erased;
1084  }
1085  else {
1086  const size_type nb_erased = m_nb_elements;
1087  m_root.reset(nullptr);
1088  m_nb_elements = 0;
1089 
1090  return nb_erased;
1091  }
1092  }
1093  else {
1094  const size_type nb_erased = current_node->as_hash_node().array_hash().size();
1095 
1096  current_node->as_hash_node().array_hash().clear();
1097  m_nb_elements -= nb_erased;
1098 
1099  clear_empty_nodes(current_node->as_hash_node());
1100 
1101  return nb_erased;
1102  }
1103  }
1104 
1105  void swap(htrie_hash& other) {
1106  using std::swap;
1107 
1108  swap(m_hash, other.m_hash);
1109  swap(m_root, other.m_root);
1110  swap(m_nb_elements, other.m_nb_elements);
1111  swap(m_max_load_factor, other.m_max_load_factor);
1112  swap(m_burst_threshold, other.m_burst_threshold);
1113  }
1114 
1115  /*
1116  * Lookup
1117  */
1118  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
1119  U& at(const CharT* key, size_type key_size) {
1120  return const_cast<U&>(static_cast<const htrie_hash*>(this)->at(key, key_size));
1121  }
1122 
1123  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
1124  const U& at(const CharT* key, size_type key_size) const {
1125  auto it_find = find(key, key_size);
1126  if(it_find != cend()) {
1127  return it_find.value();
1128  }
1129  else {
1130  throw std::out_of_range("Couldn't find key.");
1131  }
1132  }
1133 
1134  //TODO optimize
1135  template<class U = T, typename std::enable_if<has_value<U>::value>::type* = nullptr>
1136  U& access_operator(const CharT* key, size_type key_size) {
1137  auto it_find = find(key, key_size);
1138  if(it_find != cend()) {
1139  return it_find.value();
1140  }
1141  else {
1142  return insert(key, key_size, U{}).first.value();
1143  }
1144  }
1145 
1146  size_type count(const CharT* key, size_type key_size) const {
1147  if(find(key, key_size) != cend()) {
1148  return 1;
1149  }
1150  else {
1151  return 0;
1152  }
1153  }
1154 
1155  iterator find(const CharT* key, size_type key_size) {
1156  if(m_root == nullptr) {
1157  return end();
1158  }
1159 
1160  return find_impl(*m_root, key, key_size);
1161  }
1162 
1163  const_iterator find(const CharT* key, size_type key_size) const {
1164  if(m_root == nullptr) {
1165  return cend();
1166  }
1167 
1168  return find_impl(*m_root, key, key_size);
1169  }
1170 
1171  std::pair<iterator, iterator> equal_range(const CharT* key, size_type key_size) {
1172  iterator it = find(key, key_size);
1173  return std::make_pair(it, (it == end())?it:std::next(it));
1174  }
1175 
1176  std::pair<const_iterator, const_iterator> equal_range(const CharT* key, size_type key_size) const {
1177  const_iterator it = find(key, key_size);
1178  return std::make_pair(it, (it == cend())?it:std::next(it));
1179  }
1180 
1181  std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const CharT* prefix, size_type prefix_size) {
1182  if(m_root == nullptr) {
1183  return std::make_pair(prefix_end(), prefix_end());
1184  }
1185 
1186  return equal_prefix_range_impl(*m_root, prefix, prefix_size);
1187  }
1188 
1189  std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const CharT* prefix,
1190  size_type prefix_size) const
1191  {
1192  if(m_root == nullptr) {
1193  return std::make_pair(prefix_cend(), prefix_cend());
1194  }
1195 
1196  return equal_prefix_range_impl(*m_root, prefix, prefix_size);
1197  }
1198 
1199  iterator longest_prefix(const CharT* key, size_type key_size) {
1200  if(m_root == nullptr) {
1201  return end();
1202  }
1203 
1204  return longest_prefix_impl(*m_root, key, key_size);
1205  }
1206 
1207  const_iterator longest_prefix(const CharT* key, size_type key_size) const {
1208  if(m_root == nullptr) {
1209  return cend();
1210  }
1211 
1212  return longest_prefix_impl(*m_root, key, key_size);
1213  }
1214 
1215 
1216  /*
1217  * Hash policy
1218  */
1219  float max_load_factor() const {
1220  return m_max_load_factor;
1221  }
1222 
1223  void max_load_factor(float ml) {
1224  m_max_load_factor = ml;
1225  }
1226 
1227  /*
1228  * Burst policy
1229  */
1230  size_type burst_threshold() const {
1231  return m_burst_threshold;
1232  }
1233 
1234  void burst_threshold(size_type threshold) {
1235  const size_type min_burst_threshold = MIN_BURST_THRESHOLD;
1236  m_burst_threshold = std::max(min_burst_threshold, threshold);
1237  }
1238 
1239  /*
1240  * Observers
1241  */
1242  hasher hash_function() const {
1243  return m_hash;
1244  }
1245 
1246  /*
1247  * Other
1248  */
1249  template<class Serializer>
1250  void serialize(Serializer& serializer) const {
1251  serialize_impl(serializer);
1252  }
1253 
1254  template<class Deserializer>
1255  void deserialize(Deserializer& deserializer, bool hash_compatible) {
1256  deserialize_impl(deserializer, hash_compatible);
1257  }
1258 
1259 private:
1260  /**
1261  * Get the begin iterator by searching for the most left descendant node starting at search_start_node.
1262  */
1263  template<typename Iterator>
1264  Iterator cbegin(const anode& search_start_node) const noexcept {
1265  if(search_start_node.is_hash_node()) {
1266  return Iterator(search_start_node.as_hash_node());
1267  }
1268 
1269  const trie_node& tnode = search_start_node.as_trie_node().most_left_descendant_value_trie_node();
1270  if(tnode.val_node() != nullptr) {
1271  return Iterator(tnode);
1272  }
1273  else {
1274  const anode* first_child = tnode.first_child();
1275  tsl_ht_assert(first_child != nullptr);
1276 
1277  return Iterator(first_child->as_hash_node());
1278  }
1279  }
1280 
1281  /**
1282  * Get an iterator to the node that come just after the last descendant of search_start_node.
1283  */
1284  template<typename Iterator>
1285  Iterator cend(const anode& search_start_node) const noexcept {
1286  if(search_start_node.parent() == nullptr) {
1287  Iterator it;
1288  it.set_as_end_iterator();
1289 
1290  return it;
1291  }
1292 
1293  const trie_node* current_trie_node = search_start_node.parent();
1294  const anode* next_node = current_trie_node->next_child(search_start_node);
1295 
1296  while(next_node == nullptr && current_trie_node->parent() != nullptr) {
1297  const anode* current_child = current_trie_node;
1298  current_trie_node = current_trie_node->parent();
1299  next_node = current_trie_node->next_child(*current_child);
1300  }
1301 
1302  if(next_node == nullptr) {
1303  Iterator it;
1304  it.set_as_end_iterator();
1305 
1306  return it;
1307  }
1308  else {
1309  return cbegin<Iterator>(*next_node);
1310  }
1311  }
1312 
1313  prefix_iterator prefix_end() noexcept {
1314  prefix_iterator it;
1315  it.set_as_end_iterator();
1316 
1317  return it;
1318  }
1319 
1320  const_prefix_iterator prefix_cend() const noexcept {
1321  const_prefix_iterator it;
1322  it.set_as_end_iterator();
1323 
1324  return it;
1325  }
1326 
1327  size_type size_descendants(const anode& start_node) const {
1328  auto first = cbegin<const_iterator>(start_node);
1329  auto last = cend<const_iterator>(start_node);
1330 
1331  size_type nb_elements = 0;
1332  while(first != last) {
1333  if(first.m_read_trie_node_value) {
1334  nb_elements++;
1335  ++first;
1336  }
1337  else {
1338  nb_elements += first.m_current_hash_node->array_hash().size();
1339  first.skip_hash_node();
1340  }
1341  }
1342 
1343  return nb_elements;
1344  }
1345 
1346  template<class... ValueArgs>
1347  std::pair<iterator, bool> insert_impl(anode& search_start_node,
1348  const CharT* key, size_type key_size, ValueArgs&&... value_args)
1349  {
1350  anode* current_node = &search_start_node;
1351 
1352  for(size_type ikey = 0; ikey < key_size; ikey++) {
1353  if(current_node->is_trie_node()) {
1354  trie_node& tnode = current_node->as_trie_node();
1355 
1356  if(tnode.child(key[ikey]) != nullptr) {
1357  current_node = tnode.child(key[ikey]).get();
1358  }
1359  else {
1360  std::unique_ptr<hash_node> hnode(new hash_node(m_hash, m_max_load_factor));
1361  auto insert_it = hnode->array_hash().emplace_ks(key + ikey + 1, key_size - ikey - 1,
1362  std::forward<ValueArgs>(value_args)...);
1363 
1364  tnode.set_child(key[ikey], std::move(hnode));
1365  m_nb_elements++;
1366 
1367 
1368  return std::make_pair(iterator(tnode.child(key[ikey])->as_hash_node(),
1369  insert_it.first), true);
1370  }
1371  }
1372  else {
1373  return insert_in_hash_node(current_node->as_hash_node(),
1374  key + ikey, key_size - ikey, std::forward<ValueArgs>(value_args)...);
1375  }
1376  }
1377 
1378 
1379  if(current_node->is_trie_node()) {
1380  trie_node& tnode = current_node->as_trie_node();
1381  if(tnode.val_node() != nullptr) {
1382  return std::make_pair(iterator(tnode), false);
1383  }
1384  else {
1385  tnode.val_node().reset(new value_node(std::forward<ValueArgs>(value_args)...));
1386  m_nb_elements++;
1387 
1388  return std::make_pair(iterator(tnode), true);
1389  }
1390  }
1391  else {
1392  return insert_in_hash_node(current_node->as_hash_node(),
1393  "", 0, std::forward<ValueArgs>(value_args)...);
1394  }
1395  }
1396 
1397  template<class... ValueArgs>
1398  std::pair<iterator, bool> insert_in_hash_node(hash_node& hnode,
1399  const CharT* key, size_type key_size, ValueArgs&&... value_args)
1400  {
1401  if(need_burst(hnode)) {
1402  std::unique_ptr<trie_node> new_node = burst(hnode);
1403  if(hnode.parent() == nullptr) {
1404  tsl_ht_assert(m_root.get() == &hnode);
1405 
1406  m_root = std::move(new_node);
1407  return insert_impl(*m_root, key, key_size, std::forward<ValueArgs>(value_args)...);
1408  }
1409  else {
1410  trie_node* parent = hnode.parent();
1411  const CharT child_of_char = hnode.child_of_char();
1412 
1413  parent->set_child(child_of_char, std::move(new_node));
1414 
1415  return insert_impl(*parent->child(child_of_char),
1416  key, key_size, std::forward<ValueArgs>(value_args)...);
1417  }
1418  }
1419  else {
1420  auto it_insert = hnode.array_hash().emplace_ks(key, key_size,
1421  std::forward<ValueArgs>(value_args)...);
1422  if(it_insert.second) {
1423  m_nb_elements++;
1424  }
1425 
1426  return std::make_pair(iterator(hnode, it_insert.first), it_insert.second);
1427  }
1428  }
1429 
1430 
1431  iterator erase(iterator pos) {
1432  iterator next_pos = std::next(pos);
1433 
1434  if(pos.m_read_trie_node_value) {
1435  tsl_ht_assert(pos.m_current_trie_node != nullptr && pos.m_current_trie_node->val_node() != nullptr);
1436 
1437  pos.m_current_trie_node->val_node().reset(nullptr);
1438  m_nb_elements--;
1439 
1440  if(pos.m_current_trie_node->empty()) {
1441  clear_empty_nodes(*pos.m_current_trie_node);
1442  }
1443 
1444  return next_pos;
1445  }
1446  else {
1447  tsl_ht_assert(pos.m_current_hash_node != nullptr);
1448  auto next_array_hash_it = pos.m_current_hash_node->array_hash().erase(pos.m_array_hash_iterator);
1449  m_nb_elements--;
1450 
1451  if(next_array_hash_it != pos.m_current_hash_node->array_hash().end()) {
1452  // The erase on array_hash invalidated the next_pos iterator, return the right one.
1453  return iterator(*pos.m_current_hash_node, next_array_hash_it);
1454  }
1455  else {
1456  if(pos.m_current_hash_node->array_hash().empty()) {
1457  clear_empty_nodes(*pos.m_current_hash_node);
1458  }
1459 
1460  return next_pos;
1461  }
1462  }
1463  }
1464 
1465  /**
1466  * Clear all the empty nodes from the tree starting from empty_node (empty for a hash_node means that
1467  * the array hash is empty, for a trie_node it means the node doesn't have any child or value_node
1468  * associated to it).
1469  */
1470  void clear_empty_nodes(anode& empty_node) noexcept {
1471  tsl_ht_assert(!empty_node.is_trie_node() ||
1472  (empty_node.as_trie_node().empty() && empty_node.as_trie_node().val_node() == nullptr));
1473  tsl_ht_assert(!empty_node.is_hash_node() || empty_node.as_hash_node().array_hash().empty());
1474 
1475 
1476  trie_node* parent = empty_node.parent();
1477  if(parent == nullptr) {
1478  tsl_ht_assert(m_root.get() == &empty_node);
1479  tsl_ht_assert(m_nb_elements == 0);
1480  m_root.reset(nullptr);
1481  }
1482  else if(parent->val_node() != nullptr || parent->nb_children() > 1) {
1483  parent->child(empty_node.child_of_char()).reset(nullptr);
1484  }
1485  else if(parent->parent() == nullptr) {
1486  tsl_ht_assert(m_root.get() == empty_node.parent());
1487  tsl_ht_assert(m_nb_elements == 0);
1488  m_root.reset(nullptr);
1489  }
1490  else {
1491  /**
1492  * Parent is empty if we remove its empty_node child.
1493  * Put empty_node as new child of the grand parent instead of parent (move hnode up,
1494  * and delete the parent). And recurse.
1495  *
1496  * We can't just set grand_parent->child(parent->child_of_char()) to nullptr as
1497  * the grand_parent may also become empty. We don't want empty trie_node with no value_node
1498  * in the tree.
1499  */
1500  trie_node* grand_parent = parent->parent();
1501  grand_parent->set_child(parent->child_of_char(),
1502  std::move(parent->child(empty_node.child_of_char())));
1503 
1504 
1505  clear_empty_nodes(empty_node);
1506  }
1507  }
1508 
1509 
1510 
1511 
1512  iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) {
1513  return mutable_iterator(static_cast<const htrie_hash*>(this)->find_impl(search_start_node, key, key_size));
1514  }
1515 
1516  const_iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) const {
1517  const anode* current_node = &search_start_node;
1518 
1519  for(size_type ikey = 0; ikey < key_size; ikey++) {
1520  if(current_node->is_trie_node()) {
1521  const trie_node* tnode = &current_node->as_trie_node();
1522 
1523  if(tnode->child(key[ikey]) == nullptr) {
1524  return cend();
1525  }
1526  else {
1527  current_node = tnode->child(key[ikey]).get();
1528  }
1529  }
1530  else {
1531  return find_in_hash_node(current_node->as_hash_node(),
1532  key + ikey, key_size - ikey);
1533  }
1534  }
1535 
1536 
1537  if(current_node->is_trie_node()) {
1538  const trie_node& tnode = current_node->as_trie_node();
1539  return (tnode.val_node() != nullptr)?const_iterator(tnode):cend();
1540  }
1541  else {
1542  return find_in_hash_node(current_node->as_hash_node(), "", 0);
1543  }
1544  }
1545 
1546  const_iterator find_in_hash_node(const hash_node& hnode,
1547  const CharT* key, size_type key_size) const
1548  {
1549  auto it = hnode.array_hash().find_ks(key, key_size);
1550  if(it != hnode.array_hash().end()) {
1551  return const_iterator(hnode, it);
1552  }
1553  else {
1554  return cend();
1555  }
1556  }
1557 
1558 
1559  iterator longest_prefix_impl(const anode& search_start_node,
1560  const CharT* value, size_type value_size)
1561  {
1562  return mutable_iterator(static_cast<const htrie_hash*>(this)->longest_prefix_impl(search_start_node,
1563  value, value_size));
1564  }
1565 
1566  const_iterator longest_prefix_impl(const anode& search_start_node,
1567  const CharT* value, size_type value_size) const
1568  {
1569  const anode* current_node = &search_start_node;
1570  const_iterator longest_found_prefix = cend();
1571 
1572  for(size_type ivalue = 0; ivalue < value_size; ivalue++) {
1573  if(current_node->is_trie_node()) {
1574  const trie_node& tnode = current_node->as_trie_node();
1575 
1576  if(tnode.val_node() != nullptr) {
1577  longest_found_prefix = const_iterator(tnode);
1578  }
1579 
1580  if(tnode.child(value[ivalue]) == nullptr) {
1581  return longest_found_prefix;
1582  }
1583  else {
1584  current_node = tnode.child(value[ivalue]).get();
1585  }
1586  }
1587  else {
1588  const hash_node& hnode = current_node->as_hash_node();
1589 
1590  /**
1591  * Test the presence in the hash node of each substring from the
1592  * remaining [ivalue, value_size) string starting from the longest.
1593  * Also test the empty string.
1594  */
1595  for(std::size_t i = ivalue; i <= value_size; i++) {
1596  auto it = hnode.array_hash().find_ks(value + ivalue, (value_size - i));
1597  if(it != hnode.array_hash().end()) {
1598  return const_iterator(hnode, it);
1599  }
1600  }
1601 
1602  return longest_found_prefix;
1603  }
1604  }
1605 
1606  if(current_node->is_trie_node()) {
1607  const trie_node& tnode = current_node->as_trie_node();
1608 
1609  if(tnode.val_node() != nullptr) {
1610  longest_found_prefix = const_iterator(tnode);
1611  }
1612  }
1613  else {
1614  const hash_node& hnode = current_node->as_hash_node();
1615 
1616  auto it = hnode.array_hash().find_ks("", 0);
1617  if(it != hnode.array_hash().end()) {
1618  longest_found_prefix = const_iterator(hnode, it);
1619  }
1620  }
1621 
1622  return longest_found_prefix;
1623  }
1624 
1625 
1626  std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_impl(
1627  anode& search_start_node,
1628  const CharT* prefix, size_type prefix_size)
1629  {
1630  auto range = static_cast<const htrie_hash*>(this)->equal_prefix_range_impl(search_start_node,
1631  prefix, prefix_size);
1632  return std::make_pair(mutable_iterator(range.first), mutable_iterator(range.second));
1633  }
1634 
1635  std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range_impl(
1636  const anode& search_start_node,
1637  const CharT* prefix, size_type prefix_size) const
1638  {
1639  const anode* current_node = &search_start_node;
1640 
1641  for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) {
1642  if(current_node->is_trie_node()) {
1643  const trie_node* tnode = &current_node->as_trie_node();
1644 
1645  if(tnode->child(prefix[iprefix]) == nullptr) {
1646  return std::make_pair(prefix_cend(), prefix_cend());
1647  }
1648  else {
1649  current_node = tnode->child(prefix[iprefix]).get();
1650  }
1651  }
1652  else {
1653  const hash_node& hnode = current_node->as_hash_node();
1654  const_prefix_iterator begin(hnode.parent(), &hnode,
1655  hnode.array_hash().begin(), hnode.array_hash().end(),
1656  false, std::basic_string<CharT>(prefix + iprefix, prefix_size - iprefix));
1657  begin.filter_prefix();
1658 
1659  const_prefix_iterator end = cend<const_prefix_iterator>(*current_node);
1660 
1661  return std::make_pair(begin, end);
1662  }
1663  }
1664 
1665 
1666  const_prefix_iterator begin = cbegin<const_prefix_iterator>(*current_node);
1667  const_prefix_iterator end = cend<const_prefix_iterator>(*current_node);
1668 
1669  return std::make_pair(begin, end);
1670  }
1671 
1672  size_type erase_prefix_hash_node(hash_node& hnode, const CharT* prefix, size_type prefix_size) {
1673  size_type nb_erased = 0;
1674 
1675  auto it = hnode.array_hash().begin();
1676  while(it != hnode.array_hash().end()) {
1677  if(it.key_size() >= prefix_size &&
1678  std::memcmp(prefix, it.key(), prefix_size * sizeof(CharT)) == 0)
1679  {
1680  it = hnode.array_hash().erase(it);
1681  ++nb_erased;
1682  --m_nb_elements;
1683  }
1684  else {
1685  ++it;
1686  }
1687  }
1688 
1689  return nb_erased;
1690  }
1691 
1692 
1693  /*
1694  * Burst
1695  */
1696  bool need_burst(hash_node& node) const {
1697  return node.array_hash().size() >= m_burst_threshold;
1698  }
1699 
1700 
1701  /**
1702  * Burst the node and use the copy constructor instead of move constructor for the values.
1703  * Also use this method for trivial value types like int, int*, ... as it requires
1704  * less book-keeping (thus faster) than the burst using move constructors.
1705  */
1706  template<class U = T, typename std::enable_if<has_value<U>::value &&
1707  std::is_copy_constructible<U>::value &&
1708  (!std::is_nothrow_move_constructible<U>::value ||
1709  !std::is_nothrow_move_assignable<U>::value ||
1710  std::is_arithmetic<U>::value ||
1711  std::is_pointer<U>::value)>::type* = nullptr>
1712  std::unique_ptr<trie_node> burst(hash_node& node) {
1713  const std::array<size_type, ALPHABET_SIZE> first_char_count =
1714  get_first_char_count(node.array_hash().cbegin(),
1715  node.array_hash().cend());
1716 
1717 
1718  std::unique_ptr<trie_node> new_node(new trie_node());
1719  for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) {
1720  if(it.key_size() == 0) {
1721  new_node->val_node().reset(new value_node(it.value()));
1722  }
1723  else {
1724  hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]);
1725  hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, it.value());
1726  }
1727  }
1728 
1729 
1730  tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty());
1731  return new_node;
1732  }
1733 
1734  /**
1735  * Burst the node and use the move constructor and move assign operator if they don't throw.
1736  */
1737  template<class U = T, typename std::enable_if<has_value<U>::value &&
1738  std::is_nothrow_move_constructible<U>::value &&
1739  std::is_nothrow_move_assignable<U>::value &&
1740  !std::is_arithmetic<U>::value &&
1741  !std::is_pointer<U>::value>::type* = nullptr>
1742  std::unique_ptr<trie_node> burst(hash_node& node) {
1743  /**
1744  * We burst the node->array_hash() into multiple arrays hash. While doing so, we move each value in
1745  * the node->array_hash() into the new arrays hash. After each move, we save a pointer to where the value
1746  * has been moved. In case of exception, we rollback these values into the original node->array_hash().
1747  */
1748  std::vector<T*> moved_values_rollback;
1749  moved_values_rollback.reserve(node.array_hash().size());
1750 
1751  try {
1752  const std::array<size_type, ALPHABET_SIZE> first_char_count =
1753  get_first_char_count(node.array_hash().cbegin(), node.array_hash().cend());
1754 
1755 
1756  std::unique_ptr<trie_node> new_node(new trie_node());
1757  for(auto it = node.array_hash().begin(); it != node.array_hash().end(); ++it) {
1758  if(it.key_size() == 0) {
1759  new_node->val_node().reset(new value_node(std::move(it.value())));
1760  moved_values_rollback.push_back(std::addressof(new_node->val_node()->m_value));
1761  }
1762  else {
1763  hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]);
1764  auto it_insert = hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1,
1765  std::move(it.value()));
1766  moved_values_rollback.push_back(std::addressof(it_insert.first.value()));
1767  }
1768  }
1769 
1770 
1771  tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty());
1772  return new_node;
1773  }
1774  catch(...) {
1775  // Rollback the values
1776  auto it = node.array_hash().begin();
1777  for(std::size_t ivalue = 0; ivalue < moved_values_rollback.size(); ivalue++) {
1778  it.value() = std::move(*moved_values_rollback[ivalue]);
1779 
1780  ++it;
1781  }
1782 
1783  throw;
1784  }
1785  }
1786 
1787  template<class U = T, typename std::enable_if<!has_value<U>::value>::type* = nullptr>
1788  std::unique_ptr<trie_node> burst(hash_node& node) {
1789  const std::array<size_type, ALPHABET_SIZE> first_char_count =
1790  get_first_char_count(node.array_hash().begin(), node.array_hash().end());
1791 
1792 
1793  std::unique_ptr<trie_node> new_node(new trie_node());
1794  for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) {
1795  if(it.key_size() == 0) {
1796  new_node->val_node().reset(new value_node());
1797  }
1798  else {
1799  hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]);
1800  hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1);
1801  }
1802  }
1803 
1804 
1805  tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty());
1806  return new_node;
1807  }
1808 
1809  std::array<size_type, ALPHABET_SIZE> get_first_char_count(typename array_hash_type::const_iterator begin,
1810  typename array_hash_type::const_iterator end) const
1811  {
1812  std::array<size_type, ALPHABET_SIZE> count{{}};
1813  for(auto it = begin; it != end; ++it) {
1814  if(it.key_size() == 0) {
1815  continue;
1816  }
1817 
1818  count[as_position(it.key()[0])]++;
1819  }
1820 
1821  return count;
1822  }
1823 
1824 
1825  hash_node& get_hash_node_for_char(const std::array<size_type, ALPHABET_SIZE>& first_char_count,
1826  trie_node& tnode, CharT for_char)
1827  {
1828  if(tnode.child(for_char) == nullptr) {
1829  const size_type nb_buckets =
1830  size_type(
1831  std::ceil(float(first_char_count[as_position(for_char)] +
1832  HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT/2)
1833  / m_max_load_factor
1834  ));
1835 
1836  tnode.set_child(for_char, std::unique_ptr<hash_node>(
1837  new hash_node(nb_buckets, m_hash, m_max_load_factor)));
1838  }
1839 
1840  return tnode.child(for_char)->as_hash_node();
1841  }
1842 
1843  iterator mutable_iterator(const_iterator it) noexcept {
1844  // end iterator or reading from a trie node value
1845  if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) {
1846  typename array_hash_type::iterator default_it;
1847 
1848  return iterator(const_cast<trie_node*>(it.m_current_trie_node), nullptr,
1849  default_it, default_it, it.m_read_trie_node_value);
1850  }
1851  else {
1852  hash_node* hnode = const_cast<hash_node*>(it.m_current_hash_node);
1853  return iterator(const_cast<trie_node*>(it.m_current_trie_node), hnode,
1854  hnode->array_hash().mutable_iterator(it.m_array_hash_iterator),
1855  hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator),
1856  it.m_read_trie_node_value);
1857  }
1858  }
1859 
1860  prefix_iterator mutable_iterator(const_prefix_iterator it) noexcept {
1861  // end iterator or reading from a trie node value
1862  if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) {
1863  typename array_hash_type::iterator default_it;
1864 
1865  return prefix_iterator(const_cast<trie_node*>(it.m_current_trie_node), nullptr,
1866  default_it, default_it, it.m_read_trie_node_value, "");
1867  }
1868  else {
1869  hash_node* hnode = const_cast<hash_node*>(it.m_current_hash_node);
1870  return prefix_iterator(const_cast<trie_node*>(it.m_current_trie_node), hnode,
1871  hnode->array_hash().mutable_iterator(it.m_array_hash_iterator),
1872  hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator),
1873  it.m_read_trie_node_value, it.m_prefix_filter);
1874  }
1875  }
1876 
1877  template<class Serializer>
1878  void serialize_impl(Serializer& serializer) const {
1879  const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
1880  serializer(version);
1881 
1882  const slz_size_type nb_elements = m_nb_elements;
1883  serializer(nb_elements);
1884 
1885  const float max_load_factor = m_max_load_factor;
1886  serializer(max_load_factor);
1887 
1888  const slz_size_type burst_threshold = m_burst_threshold;
1889  serializer(burst_threshold);
1890 
1891 
1892  std::basic_string<CharT> str_buffer;
1893 
1894  auto it = begin();
1895  auto last = end();
1896 
1897  while(it != last) {
1898  // Serialize trie node value
1899  if(it.m_read_trie_node_value) {
1900  const CharT node_type = static_cast<typename std::underlying_type<slz_node_type>::type>(slz_node_type::TRIE_NODE);
1901  serializer(&node_type, 1);
1902 
1903  it.key(str_buffer);
1904 
1905  const slz_size_type str_size = str_buffer.size();
1906  serializer(str_size);
1907  serializer(str_buffer.data(), str_buffer.size());
1908  serialize_value(serializer, it);
1909 
1910 
1911  ++it;
1912  }
1913  // Serialize hash node values
1914  else {
1915  const CharT node_type = static_cast<typename std::underlying_type<slz_node_type>::type>(slz_node_type::HASH_NODE);
1916  serializer(&node_type, 1);
1917 
1918  it.hash_node_prefix(str_buffer);
1919 
1920  const slz_size_type str_size = str_buffer.size();
1921  serializer(str_size);
1922  serializer(str_buffer.data(), str_buffer.size());
1923 
1924  const hash_node* hnode = it.m_current_hash_node;
1925  tsl_ht_assert(hnode != nullptr);
1926  hnode->array_hash().serialize(serializer);
1927 
1928 
1929  it.skip_hash_node();
1930  }
1931  }
1932  }
1933 
1934  template<class Serializer, class U = T,
1935  typename std::enable_if<!has_value<U>::value>::type* = nullptr>
1936  void serialize_value(Serializer& /*serializer*/, const_iterator /*it*/) const {
1937  }
1938 
1939  template<class Serializer, class U = T,
1940  typename std::enable_if<has_value<U>::value>::type* = nullptr>
1941  void serialize_value(Serializer& serializer, const_iterator it) const {
1942  serializer(it.value());
1943  }
1944 
1945  template<class Deserializer>
1946  void deserialize_impl(Deserializer& deserializer, bool hash_compatible) {
1947  tsl_ht_assert(m_nb_elements == 0 && m_root == nullptr); // Current trie must be empty
1948 
1949  const slz_size_type version = deserialize_value<slz_size_type>(deserializer);
1950  // For now we only have one version of the serialization protocol.
1951  // If it doesn't match there is a problem with the file.
1952  if(version != SERIALIZATION_PROTOCOL_VERSION) {
1953  throw std::runtime_error("Can't deserialize the htrie_map/set. The protocol version header is invalid.");
1954  }
1955 
1956 
1957  const slz_size_type nb_elements = deserialize_value<slz_size_type>(deserializer);
1958  const float max_load_factor = deserialize_value<float>(deserializer);
1959  const slz_size_type burst_threshold = deserialize_value<slz_size_type>(deserializer);
1960 
1961  this->burst_threshold(numeric_cast<std::size_t>(burst_threshold, "Deserialized burst_threshold is too big."));
1962  this->max_load_factor(max_load_factor);
1963 
1964 
1965  std::vector<CharT> str_buffer;
1966  while(m_nb_elements < nb_elements) {
1967  CharT node_type_marker;
1968  deserializer(&node_type_marker, 1);
1969 
1970  static_assert(std::is_same<CharT, typename std::underlying_type<slz_node_type>::type>::value, "");
1971  const slz_node_type node_type = static_cast<slz_node_type>(node_type_marker);
1972  if(node_type == slz_node_type::TRIE_NODE) {
1973  const std::size_t str_size = numeric_cast<std::size_t>(deserialize_value<slz_size_type>(deserializer),
1974  "Deserialized str_size is too big.");
1975 
1976  str_buffer.resize(str_size);
1977  deserializer(str_buffer.data(), str_size);
1978 
1979 
1980  trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size);
1981  deserialize_value_node(deserializer, current_node);
1982  m_nb_elements++;
1983  }
1984  else if(node_type == slz_node_type::HASH_NODE) {
1985  const std::size_t str_size = numeric_cast<std::size_t>(deserialize_value<slz_size_type>(deserializer),
1986  "Deserialized str_size is too big.");
1987 
1988  if(str_size == 0) {
1989  tsl_ht_assert(m_nb_elements == 0 && !m_root);
1990 
1991  m_root.reset(new hash_node(array_hash_type::deserialize(deserializer, hash_compatible)));
1992  m_nb_elements += m_root->as_hash_node().array_hash().size();
1993 
1994  tsl_ht_assert(m_nb_elements == nb_elements);
1995  }
1996  else {
1997  str_buffer.resize(str_size);
1998  deserializer(str_buffer.data(), str_size);
1999 
2000 
2001  std::unique_ptr<hash_node> hnode(new hash_node(array_hash_type::deserialize(deserializer, hash_compatible)));
2002  m_nb_elements += hnode->array_hash().size();
2003 
2004  trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size - 1);
2005  current_node->set_child(str_buffer[str_size - 1], std::move(hnode));
2006  }
2007  }
2008  else {
2009  throw std::runtime_error("Unknown deserialized node type.");
2010  }
2011  }
2012 
2013  tsl_ht_assert(m_nb_elements == nb_elements);
2014  }
2015 
2016  trie_node* insert_prefix_trie_nodes(const CharT* prefix, std::size_t prefix_size) {
2017  if(m_root == nullptr) {
2018  m_root.reset(new trie_node());
2019  }
2020 
2021  trie_node* current_node = &m_root->as_trie_node();
2022  for(std::size_t iprefix = 0; iprefix < prefix_size; iprefix++) {
2023  if(current_node->child(prefix[iprefix]) == nullptr) {
2024  current_node->set_child(prefix[iprefix], std::unique_ptr<trie_node>(new trie_node()));
2025  }
2026 
2027  current_node = &current_node->child(prefix[iprefix])->as_trie_node();
2028  }
2029 
2030  return current_node;
2031  }
2032 
2033  template<class Deserializer, class U = T,
2034  typename std::enable_if<!has_value<U>::value>::type* = nullptr>
2035  void deserialize_value_node(Deserializer& /*deserializer*/, trie_node* current_node) {
2036  tsl_ht_assert(!current_node->val_node());
2037  current_node->val_node().reset(new value_node());
2038  }
2039 
2040  template<class Deserializer, class U = T,
2041  typename std::enable_if<has_value<U>::value>::type* = nullptr>
2042  void deserialize_value_node(Deserializer& deserializer, trie_node* current_node) {
2043  tsl_ht_assert(!current_node->val_node());
2044  current_node->val_node().reset(new value_node(deserialize_value<T>(deserializer)));
2045  }
2046 
2047  template<class U, class Deserializer>
2048  static U deserialize_value(Deserializer& deserializer) {
2049  // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword
2050  #if defined (_MSC_VER) && _MSC_VER < 1910
2051  return deserializer.Deserializer::operator()<U>();
2052  #else
2053  return deserializer.Deserializer::template operator()<U>();
2054  #endif
2055  }
2056 
2057 public:
2058  static constexpr float HASH_NODE_DEFAULT_MAX_LOAD_FACTOR = 8.0f;
2059  static const size_type DEFAULT_BURST_THRESHOLD = 16384;
2060 
2061 private:
2062 
2063  /**
2064  * Fixed size type used to represent size_type values on serialization. Need to be big enough
2065  * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms.
2066  */
2067  using slz_size_type = std::uint64_t;
2068  enum class slz_node_type: CharT { TRIE_NODE = 0, HASH_NODE = 1 };
2069 
2070  /**
2071  * Protocol version currenlty used for serialization.
2072  */
2073  static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
2074 
2075  static const size_type HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT = 32;
2076  static const size_type MIN_BURST_THRESHOLD = 4;
2077 
2078  std::unique_ptr<anode> m_root;
2079  size_type m_nb_elements;
2080  Hash m_hash;
2081  float m_max_load_factor;
2082  size_type m_burst_threshold;
2083 
2084 };
2085 
2086 } // end namespace detail_htrie_hash
2087 } // end namespace tsl
2088 
2089 #endif
static const size_type DEFAULT_BURST_THRESHOLD
Definition: htrie_hash.h:2059
htrie_hash & operator=(const htrie_hash &other)
Definition: htrie_hash.h:888
const U & at(const CharT *key, size_type key_size) const
Definition: htrie_hash.h:1124
void swap(htrie_hash &other)
Definition: htrie_hash.h:1105
htrie_hash_iterator(const htrie_hash_iterator< false, false > &other) noexcept
Definition: htrie_hash.h:582
const_iterator cbegin() const noexcept
Definition: htrie_hash.h:928
static constexpr float HASH_NODE_DEFAULT_MAX_LOAD_FACTOR
Definition: htrie_hash.h:2058
size_type erase_prefix(const CharT *prefix, size_type prefix_size)
Definition: htrie_hash.h:1046
Definition: htrie_hash.h:68
htrie_hash_iterator operator++(int)
Definition: htrie_hash.h:689
friend bool operator==(const htrie_hash_iterator &lhs, const htrie_hash_iterator &rhs)
Definition: htrie_hash.h:696
float max_load_factor() const
Definition: htrie_hash.h:1219
htrie_hash(const Hash &hash, float max_load_factor, size_type burst_threshold)
Definition: htrie_hash.h:857
htrie_hash(htrie_hash &&other) noexcept(std::is_nothrow_move_constructible< Hash >::value)
Definition: htrie_hash.h:878
pointer operator->() const
Definition: htrie_hash.h:646
size_type count(const CharT *key, size_type key_size) const
Definition: htrie_hash.h:1146
iterator erase(const_iterator first, const_iterator last)
Definition: htrie_hash.h:1023
htrie_hash(const htrie_hash &other)
Definition: htrie_hash.h:864
iterator end() noexcept
Definition: htrie_hash.h:936
hasher hash_function() const
Definition: htrie_hash.h:1242
htrie_hash & operator=(htrie_hash &&other)
Definition: htrie_hash.h:910
friend bool operator!=(const htrie_hash_iterator &lhs, const htrie_hash_iterator &rhs)
Definition: htrie_hash.h:719
iterator longest_prefix(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1199
void max_load_factor(float ml)
Definition: htrie_hash.h:1223
U & access_operator(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1136
std::basic_string< CharT > key() const
Definition: htrie_hash.h:620
iterator erase(const_iterator pos)
Definition: htrie_hash.h:1019
size_type erase(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1034
reference value() const
Definition: htrie_hash.h:628
htrie_hash_iterator(const htrie_hash_iterator< false, true > &other) noexcept
Definition: htrie_hash.h:591
void serialize(Serializer &serializer) const
Definition: htrie_hash.h:1250
size_type size() const noexcept
Definition: htrie_hash.h:962
void deserialize(Deserializer &deserializer, bool hash_compatible)
Definition: htrie_hash.h:1255
bool empty() const noexcept
Definition: htrie_hash.h:958
htrie_hash_iterator & operator++()
Definition: htrie_hash.h:650
U & at(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1119
htrie_hash_iterator() noexcept
Definition: htrie_hash.h:578
std::pair< prefix_iterator, prefix_iterator > equal_prefix_range(const CharT *prefix, size_type prefix_size)
Definition: htrie_hash.h:1181
const_iterator find(const CharT *key, size_type key_size) const
Definition: htrie_hash.h:1163
std::pair< iterator, bool > insert(const CharT *key, size_type key_size, ValueArgs &&... value_args)
Definition: htrie_hash.h:1007
iterator begin() noexcept
Definition: htrie_hash.h:920
void clear() noexcept
Definition: htrie_hash.h:1001
std::pair< iterator, iterator > equal_range(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1171
size_type max_key_size() const noexcept
Definition: htrie_hash.h:970
reference operator*() const
Definition: htrie_hash.h:641
size_type burst_threshold() const
Definition: htrie_hash.h:1230
Definition: htrie_hash.h:73
#define tsl_ht_assert(expr)
Definition: htrie_hash.h:64
void burst_threshold(size_type threshold)
Definition: htrie_hash.h:1234
void key(std::basic_string< CharT > &key_buffer_out) const
Definition: htrie_hash.h:599
const_iterator longest_prefix(const CharT *key, size_type key_size) const
Definition: htrie_hash.h:1207
const_iterator end() const noexcept
Definition: htrie_hash.h:943
Definition: htrie_hash.h:130
friend class htrie_hash
Definition: htrie_hash.h:499
std::pair< const_iterator, const_iterator > equal_range(const CharT *key, size_type key_size) const
Definition: htrie_hash.h:1176
void shrink_to_fit()
Definition: htrie_hash.h:974
Definition: htrie_hash.h:70
iterator find(const CharT *key, size_type key_size)
Definition: htrie_hash.h:1155
const_iterator cend() const noexcept
Definition: htrie_hash.h:947
std::pair< const_prefix_iterator, const_prefix_iterator > equal_prefix_range(const CharT *prefix, size_type prefix_size) const
Definition: htrie_hash.h:1189
size_type max_size() const noexcept
Definition: htrie_hash.h:966
const_iterator begin() const noexcept
Definition: htrie_hash.h:924