source: mainline/uspace/lib/cpp/include/internal/hash_table.hpp@ e29ce3d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e29ce3d was e29ce3d, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added a WIP implementation of a generic hash table that will serve as a base for all unordered associative containers

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_INTERNAL_HASH_TABLE
30#define LIBCPP_INTERNAL_HASH_TABLE
31
32#include <cstdlib>
33#include <internal/list.hpp>
34#include <memory>
35#include <utility>
36
37namespace std::aux
38{
39 /**
40 * To save code, we're going to implement one hash table
41 * for both unordered_map and unordered_set. To do this,
42 * we create one inner hash table that is oblivious to its
43 * held type (and uses a key extractor to get the key from it)
44 * and two proxies that either use plain Key type or a pair
45 * of a key and a value.
46 * Note: I am aware the naming is very unimaginative here,
47 * not my strong side :)
48 */
49
50 template<class Key, Value>
51 struct key_value_key_extractor
52 {
53 Key& operator()(pair<Key, Value>& p) const noexcept
54 {
55 return p.first;
56 }
57 };
58
59 template<class Key>
60 struct key_no_value_key_extractor
61 {
62 Key& operator()(Key& k) const noexcept
63 {
64 return k;
65 }
66 };
67
68 struct key_value_allocator
69 { /* DUMMY BODY */ };
70
71 template<>
72 struct allocator_traits<key_value_allocator>
73 {
74 template<class Alloc, class Key, class Value, class... Args>
75 static void construct(Alloc& alloc, pair<Key, Value>* ptr, Args&&... args)
76 {
77 alloc.construct(&ptr->second, forward<Args>(args)...);
78 }
79 };
80
81 struct hash_single_policy
82 {
83 // TODO: umap/uset operations
84 };
85
86 struct hash_multi_policy
87 {
88 // TODO: umultimap/umultiset operations
89 };
90
91 template<class Value, class Size>
92 struct hash_table_bucket
93 {
94 /**
95 * Note: We use a doubly linked list because
96 * we need to use hints, which point to the
97 * element after the hinted spot.
98 */
99
100 Size count;
101 link_node<Value>* head;
102
103 ~hash_table_bucket()
104 {
105 // TODO: deallocate the entire list
106 }
107 };
108
109 // TODO: iterator, const iterator, local iterator, const local iterator
110 // and also possibly two versions of each for umap and uset
111
112 template<
113 class Value, class Key, class KeyExtractor,
114 class Hasher, class KeyEq,
115 class Alloc, class Size,
116 class Iterator, class ConstIterator,
117 class LocalIterator, class LocalConstIterator,
118 class Policy
119 >
120 class hash_table
121 {
122 /**
123 * What we need:
124 * - insert
125 * - emplace
126 * - erase
127 * - iterator types
128 * - set hint (+ clear it after each operation)
129 * - copy + move
130 * - empty/size/max_size
131 * - try emplace
132 * - insert or assign
133 * - clear
134 * - find, count
135 * - equal range?
136 * - rehash/reserve
137 * - bucket stuff, local iterators (use list iterators?)
138 * - load factor stuff
139 * - multi versions for operations
140 * - eq/neq operators (or just functions that are called in the upper
141 * level operator?)
142 * - hasher and key_eq getter
143 */
144 public:
145 using value_type = Value;
146 using key_type = Key;
147 using size_type = Size;
148 using allocator_type = Alloc;
149 using key_equal = KeyEq;
150 using hasher = Hasher;
151 using key_extract = KeyExtractor;
152
153 using iterator = Iterator;
154 using const_iterator = ConstIterator;
155 using local_iterator = LocalIterator;
156 using const_local_iterator = ConstLocalIterator;
157
158 hash_table(size_type buckets, float max_load_factor)
159 {
160 // TODO: implement
161 }
162
163 bool empty() const noexcept
164 {
165 return size_ == 0;
166 }
167
168 size_type size() const noexcept
169 {
170 return size_;
171 }
172
173 template<class Allocator>
174 size_type max_size(Allocator& alloc)
175 {
176 return allocator_traits<Allocator>::max_size(alloc);
177 }
178
179 iterator begin() noexcept
180 {
181 // TODO: implement
182 }
183
184 const_iterator begin() const noexcept
185 {
186 // TODO: implement
187 }
188
189 iterator end() noexcept
190 {
191 // TODO: implement
192 }
193
194 const_iterator end() const noexcept
195 {
196 // TODO: implement
197 }
198
199 const_iterator cbegin() const noexcept
200 {
201 // TODO: implement
202 }
203
204 const_iterator cend() const noexcept
205 {
206 // TODO: implement
207 }
208
209 template<class Allocator, class... Args>
210 pair<iterator, bool> emplace(Allocator& alloc, Args&&... args)
211 {
212 // TODO: use allocator traits of allocator_type but pass alloc!
213 // TODO: also, try_emplace should be one level above (we don't know
214 // keys)
215 }
216
217 void insert(iterator it, value_type&& val)
218 {
219 // TODO: implement, make find_for_insert that will be used with this
220 // to find it to avoid unnecessary pair creations in umaps
221 // TODO: also, insert_or_assign should be done one level above
222 }
223
224 size_type erase(const key_type& key)
225 {
226 // TODO: implement
227 }
228
229 iterator erase(const_iterator it)
230 {
231 // TODO: implement
232 }
233
234 void clear() noexcept
235 {
236 // TODO: implement
237 }
238
239 template<class Allocator>
240 void swap(hash_table& other)
241 noexcept(allocator_traits<Allocator>::is_always_equal::value &&
242 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
243 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
244 {
245 // TODO: implement
246 }
247
248 hasher hash_function() const
249 {
250 return hasher_;
251 }
252
253 key_equal key_eq() const
254 {
255 return key_eq_;
256 }
257
258 iterator find(const key_type& key)
259 {
260 // TODO: implement
261 }
262
263 const_iterator find(const key_type& key) const
264 {
265 // TODO: implement
266 }
267
268 size_type count(const key_type& key) const
269 {
270 // TODO: implement
271 }
272
273 pair<iterator, iterator> equal_range(const key_type& key)
274 {
275 // TODO: implement
276 }
277
278 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
279 {
280 // TODO: implement
281 }
282
283 size_type bucket_count() const noexcept
284 {
285 return bucket_count_;
286 }
287
288 size_type max_bucket_count() const noexcept
289 {
290 // TODO: implement
291 return 0;
292 }
293
294 size_type bucket_size(size_type n) const
295 {
296 // TODO: implement
297 }
298
299 size_type bucket(const key_type& key) const
300 {
301 // TODO: implement
302 }
303
304 local_iterator begin(size_type n)
305 {
306 // TODO: implement
307 }
308
309 const_local_iterator begin(size_type n) const
310 {
311 // TODO: implement
312 }
313
314 local_iterator end(size_type n) const
315 {
316 // TODO: implement
317 }
318
319 const_local_iterator cbegin(size_type n) const
320 {
321 // TODO: implement
322 }
323
324 const_local_iterator cend(size_type n) const
325 {
326 // TODO: implement
327 }
328
329 float load_factor() const noexcept
330 {
331 return size_ / static_cast<float>(bucket_count_);
332 }
333
334 float max_load_factor() const noexcept
335 {
336 return max_load_factor_;
337 }
338
339 void max_load_factor(float factor)
340 {
341 /**
342 * Note: According to the standard, this function
343 * can have no effect.
344 */
345 }
346
347 void rehash(size_type n)
348 {
349 // TODO: implement
350 }
351
352 void reserve(size_type n)
353 {
354 // TODO: implement
355 }
356
357 bool is_eq_to(hash_table& other)
358 {
359 // TODO: implement
360 }
361
362 ~hash_table()
363 {
364 // Lists are deleted in ~hash_table_bucket.
365 delete[] table_;
366 }
367
368 void set_hint(const_iterator hint)
369 {
370 // TODO: hint_ should be a ptr and we extract it here,
371 // then set it to nullptr after each operation
372 }
373
374 private:
375 hash_table_bucket<value_type, size_type>* table_;
376 size_type bucket_count_;
377 size_type size_;
378 hasher hasher_;
379 key_equal key_eq_;
380 float max_load_factor_;
381 };
382
383 template<
384 class Key, class Value,
385 class Hasher, class KeyEq,
386 class Alloc, class Size
387 >
388 class hash_table_with_value
389 {
390 public:
391 using extractor_type = key_value_key_extractor<Key, Value>;
392 using table_type = hash_table<
393 pair<Key, Value>, extractor_type,
394 KeyEq, key_value_allocator, Size
395 >;
396
397 private:
398 table_type table_;
399 extractor_type extractor_;
400 };
401
402 template<
403 class Key,
404 class Hasher, class KeyEq,
405 class Alloc, class Size
406 >
407 class hash_table_without_value
408 {
409 using extractor_type = key_no_value_key_extractor<Key>;
410 using table_type = hash_table<
411 Key, extractor_type, KeyEq, Alloc, Size
412 >;
413
414 private:
415 table_type table_;
416 };
417}
418
419#endif
Note: See TracBrowser for help on using the repository browser.