44#include < utility>
55#include < cstring> // memcpy
66
7- namespace bi = boost::intrusive;
8-
97namespace hpack {
108
11- struct dynamic_table_t ::entry_t : bi::set_base_hook<bi::link_mode<bi::normal_link>> {
9+ struct dynamic_table_t ::entry_t : entry_set_hook {
1210 const size_type name_end;
1311 const size_type value_end;
1412 const size_t _insert_c;
@@ -46,6 +44,27 @@ struct dynamic_table_t::entry_t : bi::set_base_hook<bi::link_mode<bi::normal_lin
4644 }
4745};
4846
47+ std::string_view dynamic_table_t::key_of_entry::operator ()(const dynamic_table_t ::entry_t & v) const noexcept {
48+ return v.name ();
49+ }
50+
51+ static size_t hash_calc (std::string_view bytes) noexcept {
52+ // standard hash is bad sometimes
53+ constexpr uint64_t fnv_offset_basis = 14695981039346656037ULL ;
54+ constexpr uint64_t fnv_prime = 1099511628211ULL ;
55+ size_t hash = fnv_offset_basis;
56+ for (auto byte : bytes) {
57+ hash ^= static_cast <uint64_t >(byte);
58+ hash *= fnv_prime;
59+ }
60+ return hash;
61+ }
62+
63+ size_t dynamic_table_t::hash_by_namevalue::operator ()(
64+ dynamic_table_t ::key_of_entry::type str) const noexcept {
65+ return hash_calc (str);
66+ }
67+
4968// precondition: 'e' now in entries
5069index_type dynamic_table_t::indexof (const dynamic_table_t ::entry_t & e) const noexcept {
5170 return static_table_t ::first_unused_index + (_insert_count - e._insert_c );
@@ -61,12 +80,10 @@ static size_type entry_size(const dynamic_table_t::entry_t& entry) noexcept {
6180 return entry.value_end + 32 ;
6281}
6382
64- table_entry dynamic_table_t::key_of_entry::operator ()(const dynamic_table_t ::entry_t & v) const noexcept {
65- return {v.name (), v.value ()};
66- }
67-
6883dynamic_table_t::dynamic_table_t (size_type max_size, std::pmr::memory_resource* m) noexcept
69- : _current_size(0 ),
84+ : buckets(initial_buckets_count),
85+ set ({buckets.data (), buckets.size ()}),
86+ _current_size(0 ),
7087 _max_size(max_size),
7188 _user_protocol_max_size(max_size),
7289 _insert_count(0 ),
@@ -109,6 +126,14 @@ index_type dynamic_table_t::add_entry(std::string_view name, std::string_view va
109126 evict_until_fits_into (_max_size - new_entry_size);
110127 entries.push_back (entry_t::create (name, value, ++_insert_count, _resource));
111128 set.insert (*entries.back ());
129+ if (entries.size () > buckets.size () / 2 ) {
130+ // https://github.com/boostorg/intrusive/issues/96
131+ // workaround:
132+ // unordered set bucket copy(move) ctor does nothing, so .resize will be UB
133+ decltype (buckets) new_buckets (buckets.size () * 2 );
134+ set.rehash ({new_buckets.data (), new_buckets.size ()});
135+ buckets = std::move (new_buckets);
136+ }
112137 _current_size += new_entry_size;
113138 return static_table_t ::first_unused_index;
114139}
@@ -131,36 +156,26 @@ void dynamic_table_t::update_size(size_type new_max_size) {
131156
132157find_result_t dynamic_table_t::find (std::string_view name, std::string_view value) noexcept {
133158 find_result_t r;
134- auto it = set.find (table_entry{name, value});
135- if (it == set.end ())
136- return r;
137- if (name == it->name ()) {
138- r.header_name_index = indexof (*it);
139- if (value == it->value ())
140- r.value_indexed = true ;
159+ auto i = set.bucket (name);
160+ auto b = set.begin (i);
161+ auto e = set.end (i);
162+ for (; b != e; ++b) {
163+ if (b->name () == name) {
164+ r.header_name_index = indexof (*b);
165+ if (b->value () == value) {
166+ r.value_indexed = true ;
167+ return r;
168+ }
169+ }
141170 }
142171 return r;
143172}
144173
145174find_result_t dynamic_table_t::find (index_type name, std::string_view value) noexcept {
146175 assert (name <= current_max_index ());
147- find_result_t r;
148- if (name < static_table_t ::first_unused_index || name > current_max_index () || name == 0 )
149- return r;
150- table_entry e = get_entry (name);
151- if (e.value == value) {
152- r.header_name_index = name;
153- r.value_indexed = true ;
154- return r;
155- }
156- auto it = set.find (table_entry{e.name , value});
157- assert (it != set.end ());
158- if (e.name == it->name ()) {
159- r.header_name_index = indexof (*it);
160- if (value == it->value ())
161- r.value_indexed = true ;
162- }
163- return r;
176+ if (name == 0 ) [[unlikely]]
177+ return {};
178+ return find (get_entry (name).name , value);
164179}
165180
166181void dynamic_table_t::reset () noexcept {
@@ -175,15 +190,17 @@ void dynamic_table_t::evict_until_fits_into(size_type bytes) noexcept {
175190 size_type i = 0 ;
176191 for (; _current_size > bytes; ++i) {
177192 _current_size -= entry_size (*entries[i]);
178- set.erase (set.s_iterator_to (*entries[i]));
193+ set.erase (set.iterator_to (*entries[i]));
179194 entry_t::destroy (entries[i], _resource);
180195 }
181196 // evicts should be rare operation
182197 entries.erase (entries.begin (), entries.begin () + i);
183198}
184199
185200table_entry dynamic_table_t::get_entry (index_type index) const noexcept {
186- assert (index >= static_table_t ::first_unused_index && index <= current_max_index ());
201+ assert (index != 0 && index <= current_max_index ());
202+ if (index < static_table_t ::first_unused_index)
203+ return static_table_t::get_entry (index);
187204 auto & e = *(&entries.back () - (index - static_table_t ::first_unused_index));
188205 return table_entry{e->name (), e->value ()};
189206}
0 commit comments