Changeset 871cfe0c in mainline
- Timestamp:
- 2018-07-05T21:41:21Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- e9027b5
- Parents:
- e29ce3d
- git-author:
- Dzejrou <dzejrou@…> (2018-04-23 18:12:24)
- git-committer:
- Dzejrou <dzejrou@…> (2018-07-05 21:41:21)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/cpp/include/internal/hash_table.hpp
re29ce3d r871cfe0c 32 32 #include <cstdlib> 33 33 #include <internal/list.hpp> 34 #include <iterator> 34 35 #include <memory> 36 #include <tuple> 35 37 #include <utility> 36 38 … … 48 50 */ 49 51 50 template<class Key, Value>52 template<class Key, class Value> 51 53 struct key_value_key_extractor 52 54 { … … 68 70 struct key_value_allocator 69 71 { /* 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_policy82 {83 // TODO: umap/uset operations84 };85 86 struct hash_multi_policy87 {88 // TODO: umultimap/umultiset operations89 };90 72 91 73 template<class Value, class Size> … … 98 80 */ 99 81 100 Size count; 101 link_node<Value>* head; 82 list_node<Value>* head; 83 84 Size size() const noexcept 85 { 86 // TODO: implement 87 } 88 89 void append(list_node<Value>* node) 90 { 91 if (!head) 92 head = node; 93 else 94 head->append(node); 95 } 102 96 103 97 ~hash_table_bucket() … … 107 101 }; 108 102 109 // TODO: iterator, const iterator, local iterator, const local iterator 110 // and also possibly two versions of each for umap and uset 103 struct hash_single_policy 104 { 105 // TODO: umap/uset operations 106 107 template<class Table, class Key> 108 static typename Table::size_type count(const Table& table, const Key& key) 109 { 110 return table.find(key) == table.end() ? 0 : 1; 111 } 112 113 template<class Table, class Key> 114 static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key) 115 { 116 auto idx = table.get_bucket_idx_(key); 117 return make_tuple( 118 &table.table_[idx], 119 table.table_[idx].head, 120 idx 121 ); 122 } 123 }; 124 125 struct hash_multi_policy 126 { 127 // TODO: umultimap/umultiset operations 128 129 template<class Table, class Key> 130 static typename Table::size_type count(const Table& table, const Key& key) 131 { 132 auto head = table.table_[get_bucket_idx_(key)].head; 133 if (!head) 134 return 0; 135 136 auto current = head; 137 typename Table::size_type res = 0; 138 do 139 { 140 if (table.key_eq_(key, table.key_extractor_(current->value))) 141 ++res; 142 143 current = current->next; 144 } 145 while (current != head); 146 147 return res; 148 } 149 150 template<class Table, class Key> 151 static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key) 152 { 153 // TODO: find the right bucket, in that, find the right 154 // link (if key is already in it, place the new copy 155 // next to it, otherwise just return head) 156 } 157 }; 158 159 template<class Value, class ConstReference, class ConstPointer, class Size> 160 class hash_table_const_iterator 161 { 162 public: 163 using value_type = Value; 164 using size_type = Size; 165 using const_reference = ConstReference; 166 using const_pointer = ConstPointer; 167 using difference_type = ptrdiff_t; 168 169 using iterator_category = forward_iterator_tag; 170 171 hash_table_const_iterator(hash_table_bucket<value_type, size_type>* table = nullptr, 172 size_type idx = size_type{}, size_type max_idx = size_type{}, 173 list_node<value_type>* current = nullptr) 174 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current} 175 { /* DUMMY BODY */ } 176 177 hash_table_const_iterator(const hash_table_const_iterator&) = default; 178 hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default; 179 180 const_reference operator*() const 181 { 182 return current_->value; 183 } 184 185 const_pointer operator->() const 186 { 187 return ¤t_->value; 188 } 189 190 hash_table_const_iterator& operator++() 191 { 192 current_ = current_->next; 193 if (current_ == table_[idx_].head) 194 { 195 if (idx_ < max_idx_) 196 { 197 while (!table_[++idx_].head) 198 { /* DUMMY BODY */ } 199 200 if (idx_ < max_idx_) 201 current_ = table_[idx_].head; 202 else 203 current_ = nullptr; 204 } 205 else 206 current_ = nullptr; 207 } 208 209 return *this; 210 } 211 212 hash_table_const_iterator operator++(int) 213 { 214 auto tmp_current = current_; 215 auto tmp_idx = idx_; 216 217 current_ = current_->next; 218 if (current_ == table_[idx_].head) 219 { 220 if (idx_ < max_idx_) 221 { 222 while (!table_[++idx_].head) 223 { /* DUMMY BODY */ } 224 225 if (idx_ < max_idx_) 226 current_ = table_[idx_].head; 227 else 228 current_ = nullptr; 229 } 230 else 231 current_ = nullptr; 232 } 233 234 return hash_table_const_iterator{ 235 table_, tmp_idx, max_idx_, tmp_current 236 }; 237 } 238 239 list_node<value_type>* node() 240 { 241 return current_; 242 } 243 244 const list_node<value_type>* node() const 245 { 246 return current_; 247 } 248 249 private: 250 hash_table_bucket<value_type, size_type>* table_; 251 size_type idx_; 252 size_type max_idx_; 253 list_node<value_type>* current_; 254 }; 255 256 template<class Value, class ConstRef, class ConstPtr, class Size> 257 bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs, 258 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs) 259 { 260 return lhs.node() == rhs.node(); 261 } 262 263 template<class Value, class ConstRef, class ConstPtr, class Size> 264 bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs, 265 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs) 266 { 267 return !(lhs == rhs); 268 } 269 270 template<class Value, class Reference, class Pointer, class Size> 271 class hash_table_iterator 272 { 273 public: 274 using value_type = Value; 275 using size_type = Size; 276 using reference = Reference; 277 using pointer = Pointer; 278 using difference_type = ptrdiff_t; 279 280 using iterator_category = forward_iterator_tag; 281 282 hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr, 283 size_type idx = size_type{}, size_type max_idx = size_type{}, 284 list_node<value_type>* current = nullptr) 285 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current} 286 { /* DUMMY BODY */ } 287 288 hash_table_iterator(const hash_table_iterator&) = default; 289 hash_table_iterator& operator=(const hash_table_iterator&) = default; 290 291 reference operator*() 292 { 293 return current_->value; 294 } 295 296 pointer operator->() 297 { 298 return ¤t_->value; 299 } 300 301 hash_table_iterator& operator++() 302 { 303 current_ = current_->next; 304 if (current_ == table_[idx_].head) 305 { 306 if (idx_ < max_idx_) 307 { 308 while (!table_[++idx_].head) 309 { /* DUMMY BODY */ } 310 311 if (idx_ < max_idx_) 312 current_ = table_[idx_].head; 313 else 314 current_ = nullptr; 315 } 316 else 317 current_ = nullptr; 318 } 319 320 return *this; 321 } 322 323 hash_table_iterator operator++(int) 324 { 325 auto tmp_current = current_; 326 auto tmp_idx = idx_; 327 328 current_ = current_->next; 329 if (current_ == table_[idx_].head) 330 { 331 if (idx_ < max_idx_) 332 { 333 while (!table_[++idx_].head) 334 { /* DUMMY BODY */ } 335 336 if (idx_ < max_idx_) 337 current_ = table_[idx_].head; 338 else 339 current_ = nullptr; 340 } 341 else 342 current_ = nullptr; 343 } 344 345 return hash_table_iterator{ 346 table_, tmp_idx, max_idx_, tmp_current 347 }; 348 } 349 350 list_node<value_type>* node() 351 { 352 return current_; 353 } 354 355 const list_node<value_type>* node() const 356 { 357 return current_; 358 } 359 360 template<class ConstRef, class ConstPtr> 361 operator hash_table_const_iterator< 362 Value, ConstRef, ConstPtr, Size 363 >() const 364 { 365 return hash_table_const_iterator{ 366 table_, idx_, max_idx_, current_ 367 }; 368 } 369 370 private: 371 hash_table_bucket<value_type, size_type>* table_; 372 size_type idx_; 373 size_type max_idx_; 374 list_node<value_type>* current_; 375 }; 376 377 template<class Value, class Ref, class Ptr, class Size> 378 bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs, 379 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs) 380 { 381 return lhs.node() == rhs.node(); 382 } 383 384 template<class Value, class Ref, class Ptr, class Size> 385 bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs, 386 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs) 387 { 388 return !(lhs == rhs); 389 } 390 391 template<class Value, class ConstReference, class ConstPointer> 392 class hash_table_const_local_iterator 393 { 394 public: 395 using value_type = Value; 396 using const_reference = ConstReference; 397 using const_pointer = ConstPointer; 398 using difference_type = ptrdiff_t; 399 400 using iterator_category = forward_iterator_tag; 401 402 // TODO: requirement for forward iterator is default constructibility, fix others! 403 hash_table_const_local_iterator(list_node<value_type>* head = nullptr, 404 list_node<value_type>* current = nullptr) 405 : head_{head}, current_{current} 406 { /* DUMMY BODY */ } 407 408 hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default; 409 hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default; 410 411 const_reference operator*() const 412 { 413 return current_->value; 414 } 415 416 const_pointer operator->() const 417 { 418 return ¤t_->value; 419 } 420 421 hash_table_const_local_iterator& operator++() 422 { 423 current_ = current_->next; 424 if (current_ == head_) 425 current_ = nullptr; 426 427 return *this; 428 } 429 430 hash_table_const_local_iterator operator++(int) 431 { 432 auto tmp = current_; 433 current_ = current_->next; 434 if (current_ == head_) 435 current_ = nullptr; 436 437 return hash_table_const_local_iterator{head_, tmp}; 438 } 439 440 list_node<value_type>* node() 441 { 442 return current_; 443 } 444 445 const list_node<value_type>* node() const 446 { 447 return current_; 448 } 449 450 private: 451 list_node<value_type>* head_; 452 list_node<value_type>* current_; 453 }; 454 455 template<class Value, class ConstRef, class ConstPtr> 456 bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs, 457 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs) 458 { 459 return lhs.node() == rhs.node(); 460 } 461 462 template<class Value, class ConstRef, class ConstPtr> 463 bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs, 464 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs) 465 { 466 return !(lhs == rhs); 467 } 468 469 template<class Value, class Reference, class Pointer> 470 class hash_table_local_iterator 471 { 472 public: 473 using value_type = Value; 474 using reference = Reference; 475 using pointer = Pointer; 476 using difference_type = ptrdiff_t; 477 478 using iterator_category = forward_iterator_tag; 479 480 hash_table_local_iterator(list_node<value_type>* head = nullptr, 481 list_node<value_type>* current = nullptr) 482 : head_{head}, current_{current} 483 { /* DUMMY BODY */ } 484 485 hash_table_local_iterator(const hash_table_local_iterator&) = default; 486 hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default; 487 488 reference operator*() 489 { 490 return current_->value; 491 } 492 493 pointer operator->() 494 { 495 return ¤t_->value; 496 } 497 498 hash_table_local_iterator& operator++() 499 { 500 current_ = current_->next; 501 if (current_ == head_) 502 current_ = nullptr; 503 504 return *this; 505 } 506 507 hash_table_local_iterator operator++(int) 508 { 509 auto tmp = current_; 510 current_ = current_->next; 511 if (current_ == head_) 512 current_ = nullptr; 513 514 return hash_table_local_iterator{head_, tmp}; 515 } 516 517 list_node<value_type>* node() 518 { 519 return current_; 520 } 521 522 const list_node<value_type>* node() const 523 { 524 return current_; 525 } 526 527 template<class ConstRef, class ConstPtr> 528 operator hash_table_const_local_iterator< 529 Value, ConstRef, ConstPtr 530 >() const 531 { 532 return hash_table_const_local_iterator{head_, current_}; 533 } 534 535 private: 536 list_node<value_type>* head_; 537 list_node<value_type>* current_; 538 }; 539 540 template<class Value, class Ref, class Ptr> 541 bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs, 542 const hash_table_local_iterator<Value, Ref, Ptr>& rhs) 543 { 544 return lhs.node() == rhs.node(); 545 } 546 547 template<class Value, class Ref, class Ptr> 548 bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs, 549 const hash_table_local_iterator<Value, Ref, Ptr>& rhs) 550 { 551 return !(lhs == rhs); 552 } 111 553 112 554 template< … … 115 557 class Alloc, class Size, 116 558 class Iterator, class ConstIterator, 117 class LocalIterator, class LocalConstIterator,559 class LocalIterator, class ConstLocalIterator, 118 560 class Policy 119 561 > 120 562 class hash_table 121 563 { 122 /**123 * What we need:124 * - insert125 * - emplace126 * - erase127 * - iterator types128 * - set hint (+ clear it after each operation)129 * - copy + move130 * - empty/size/max_size131 * - try emplace132 * - insert or assign133 * - clear134 * - find, count135 * - equal range?136 * - rehash/reserve137 * - bucket stuff, local iterators (use list iterators?)138 * - load factor stuff139 * - multi versions for operations140 * - eq/neq operators (or just functions that are called in the upper141 * level operator?)142 * - hasher and key_eq getter143 */144 564 public: 145 565 using value_type = Value; … … 156 576 using const_local_iterator = ConstLocalIterator; 157 577 158 hash_table(size_type buckets, float max_load_factor) 159 { 160 // TODO: implement 161 } 578 using hint_type = tuple< 579 hash_table_bucket<value_type, size_type>*, 580 list_node<value_type>*, size_type 581 >; 582 583 hash_table(size_type buckets, float max_load_factor = 1.f) 584 : table_{new hash_table_bucket<value_type, size_type>[buckets]}, 585 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{}, 586 key_extractor_{}, max_load_factor_{max_load_factor} 587 { /* DUMMY BODY */ } 162 588 163 589 bool empty() const noexcept … … 179 605 iterator begin() noexcept 180 606 { 181 // TODO: implement 607 return iterator{ 608 table_, size_type{}, bucket_count_, 609 table_[0].head 610 }; 182 611 } 183 612 184 613 const_iterator begin() const noexcept 185 614 { 186 // TODO: implement615 return cbegin(); 187 616 } 188 617 189 618 iterator end() noexcept 190 619 { 191 // TODO: implement620 return iterator{}; 192 621 } 193 622 194 623 const_iterator end() const noexcept 195 624 { 196 // TODO: implement625 return cend(); 197 626 } 198 627 199 628 const_iterator cbegin() const noexcept 200 629 { 201 // TODO: implement 630 return const_iterator{ 631 table_, size_type{}, bucket_count_, 632 table_[0].head 633 }; 202 634 } 203 635 204 636 const_iterator cend() const noexcept 205 637 { 206 // TODO: implement638 return const_iterator{}; 207 639 } 208 640 209 641 template<class Allocator, class... Args> 210 pair<iterator, bool>emplace(Allocator& alloc, Args&&... args)642 iterator emplace(Allocator& alloc, Args&&... args) 211 643 { 212 644 // TODO: use allocator traits of allocator_type but pass alloc! … … 215 647 } 216 648 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 649 void insert(const hint_type& where, const value_type& val) 650 { 651 if (!hint_ok_(where)) 652 return; 653 654 auto node = new list_node<value_type>{val}; 655 if (get<1>(where) == nullptr) // Append here will create a new head. 656 get<0>(where)->append(node); 657 else // Prepending before an exact position is common in the standard. 658 get<1>(where)->prepend(node); 659 660 ++size_; 661 } 662 663 void insert(const hint_type& where, value_type&& val) 664 { 665 if (!hint_ok_(where)) 666 return; 667 668 auto node = new list_node<value_type>{forward<value_type>(val)}; 669 if (get<1>(where) == nullptr) 670 get<0>(where)->append(node); 671 else 672 get<1>(where)->prepend(node); 673 674 ++size_; 222 675 } 223 676 … … 234 687 void clear() noexcept 235 688 { 236 // TODO: implement 689 delete[] table_; 690 691 size_ = size_type{}; 692 table_ = new hash_table_bucket<value_type, size_type>[bucket_count_]; 237 693 } 238 694 … … 243 699 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>()))) 244 700 { 245 // TODO: implement 701 std::swap(table_, other.table_); 702 std::swap(bucket_count_, other.bucket_count_); 703 std::swap(size_, other.size_); 704 std::swap(hasher_, other.hasher_); 705 std::swap(key_eq_, other.key_eq_); 706 std::swap(max_load_factor_, other.max_load_factor_); 246 707 } 247 708 … … 268 729 size_type count(const key_type& key) const 269 730 { 270 // TODO: implement731 return Policy::count(*this, key); 271 732 } 272 733 … … 294 755 size_type bucket_size(size_type n) const 295 756 { 296 // TODO: implement757 return table_[n].size(); 297 758 } 298 759 299 760 size_type bucket(const key_type& key) const 300 761 { 301 // TODO: implement762 return get_bucket_idx_(key); 302 763 } 303 764 304 765 local_iterator begin(size_type n) 305 766 { 306 // TODO: implement767 return local_iterator{table_[n].head, table_[n].head}; 307 768 } 308 769 309 770 const_local_iterator begin(size_type n) const 310 771 { 311 // TODO: implement 312 } 313 314 local_iterator end(size_type n) const 315 { 316 // TODO: implement 772 return cbegin(n); 773 } 774 775 local_iterator end(size_type n) 776 { 777 return local_iterator{}; 778 } 779 780 const_local_iterator end(size_type n) const 781 { 782 return cend(n); 317 783 } 318 784 319 785 const_local_iterator cbegin(size_type n) const 320 786 { 321 // TODO: implement787 return const_local_iterator{table_[n].head, table_[n].head}; 322 788 } 323 789 324 790 const_local_iterator cend(size_type n) const 325 791 { 326 // TODO: implement792 return const_local_iterator{}; 327 793 } 328 794 … … 348 814 { 349 815 // TODO: implement 816 // TODO: exception thrown in rehash means the rehash 817 // has no effect, so we create a new table, insert in it 818 // and then swap 350 819 } 351 820 … … 358 827 { 359 828 // TODO: implement 829 return false; 360 830 } 361 831 … … 372 842 } 373 843 374 private: 844 hint_type find_insertion_spot(const key_type& key) 845 { 846 return Policy::find_insertion_spot(*this, key); 847 } 848 849 hint_type find_insertion_spot(key_type&& key) 850 { 851 return Policy::find_insertion_spot(*this, key); 852 } 853 854 /* private: */ 375 855 hash_table_bucket<value_type, size_type>* table_; 376 856 size_type bucket_count_; … … 378 858 hasher hasher_; 379 859 key_equal key_eq_; 860 key_extract key_extractor_; 380 861 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_; 862 863 size_type get_bucket_idx_(const key_type& key) const 864 { 865 return hasher_(key) % bucket_count_; 866 } 867 868 bool hint_ok_(const hint_type& hint) 869 { 870 // TODO: pass this to the policy, because the multi policy 871 // will need to check if a similar key is close, 872 // that is something like: 873 // return get<1>(hint)->prev->key == key || !bucket.contains(key) 874 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_; 875 } 876 877 // Praise C++11 for this. 878 friend Policy; 416 879 }; 417 880 } 418 881 882 namespace std 883 { 884 template<> 885 struct allocator_traits<aux::key_value_allocator> 886 { 887 template<class Alloc, class Key, class Value, class... Args> 888 static void construct(Alloc& alloc, pair<Key, Value>* ptr, Args&&... args) 889 { 890 alloc.construct(&ptr->second, forward<Args>(args)...); 891 } 892 }; 893 } 894 419 895 #endif
Note:
See TracChangeset
for help on using the changeset viewer.