hb-map.hh (15371B)
1 /* 2 * Copyright © 2018 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #ifndef HB_MAP_HH 28 #define HB_MAP_HH 29 30 #include "hb.hh" 31 32 #include "hb-set.hh" 33 34 35 /* 36 * hb_hashmap_t 37 */ 38 39 extern HB_INTERNAL const hb_codepoint_t minus_1; 40 41 template <typename K, typename V, 42 bool minus_one = false> 43 struct hb_hashmap_t 44 { 45 static constexpr bool realloc_move = true; 46 47 hb_hashmap_t () { init (); } 48 ~hb_hashmap_t () { fini (); } 49 50 void _copy (const hb_hashmap_t& o) 51 { 52 if (unlikely (!o.mask)) return; 53 54 if (hb_is_trivially_copy_assignable (item_t)) 55 { 56 items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); 57 if (unlikely (!items)) 58 { 59 successful = false; 60 return; 61 } 62 population = o.population; 63 occupancy = o.occupancy; 64 mask = o.mask; 65 prime = o.prime; 66 max_chain_length = o.max_chain_length; 67 memcpy (items, o.items, sizeof (item_t) * (mask + 1)); 68 return; 69 } 70 71 alloc (o.population); hb_copy (o, *this); 72 } 73 74 hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { _copy (o); } 75 hb_hashmap_t& operator= (const hb_hashmap_t& o) 76 { 77 reset (); 78 if (!items) { _copy (o); return *this; } 79 alloc (o.population); hb_copy (o, *this); return *this; 80 } 81 82 hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); } 83 hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; } 84 85 hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t () 86 { 87 for (auto&& item : lst) 88 set (item.first, item.second); 89 } 90 template <typename Iterable, 91 hb_requires (hb_is_iterable (Iterable))> 92 hb_hashmap_t (const Iterable &o) : hb_hashmap_t () 93 { 94 auto iter = hb_iter (o); 95 if (iter.is_random_access_iterator || iter.has_fast_len) 96 alloc (hb_len (iter)); 97 hb_copy (iter, *this); 98 } 99 100 struct item_t 101 { 102 K key; 103 uint32_t is_real_ : 1; 104 uint32_t is_used_ : 1; 105 uint32_t hash : 30; 106 V value; 107 108 item_t () : key (), 109 is_real_ (false), is_used_ (false), 110 hash (0), 111 value () {} 112 113 // Needed for https://github.com/harfbuzz/harfbuzz/issues/4138 114 K& get_key () { return key; } 115 V& get_value () { return value; } 116 117 bool is_used () const { return is_used_; } 118 void set_used (bool is_used) { is_used_ = is_used; } 119 void set_real (bool is_real) { is_real_ = is_real; } 120 bool is_real () const { return is_real_; } 121 122 template <bool v = minus_one, 123 hb_enable_if (v == false)> 124 static inline const V& default_value () { return Null(V); }; 125 template <bool v = minus_one, 126 hb_enable_if (v == true)> 127 static inline const V& default_value () 128 { 129 static_assert (hb_is_same (V, hb_codepoint_t), ""); 130 return minus_1; 131 }; 132 133 bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); } 134 bool operator == (const item_t &o) const { return *this == o.key; } 135 hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); } 136 hb_pair_t<const K &, V &> get_pair_ref() { return hb_pair_t<const K &, V &> (key, value); } 137 138 uint32_t total_hash () const 139 { return (hash * 31u) + hb_hash (value); } 140 141 static constexpr bool is_trivially_constructible = (hb_is_trivially_constructible(K) && hb_is_trivially_constructible(V)); 142 }; 143 144 hb_object_header_t header; 145 bool successful; /* Allocations successful */ 146 unsigned short max_chain_length; 147 unsigned int population; /* Not including tombstones. */ 148 unsigned int occupancy; /* Including tombstones. */ 149 unsigned int mask; 150 unsigned int prime; 151 item_t *items; 152 153 friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) noexcept 154 { 155 if (unlikely (!a.successful || !b.successful)) 156 return; 157 hb_swap (a.max_chain_length, b.max_chain_length); 158 hb_swap (a.population, b.population); 159 hb_swap (a.occupancy, b.occupancy); 160 hb_swap (a.mask, b.mask); 161 hb_swap (a.prime, b.prime); 162 hb_swap (a.items, b.items); 163 } 164 void init () 165 { 166 hb_object_init (this); 167 168 successful = true; 169 max_chain_length = 0; 170 population = occupancy = 0; 171 mask = 0; 172 prime = 0; 173 items = nullptr; 174 } 175 void fini () 176 { 177 hb_object_fini (this); 178 179 if (likely (items)) 180 { 181 unsigned size = mask + 1; 182 for (unsigned i = 0; i < size; i++) 183 items[i].~item_t (); 184 hb_free (items); 185 items = nullptr; 186 } 187 population = occupancy = 0; 188 } 189 190 hb_hashmap_t& reset () 191 { 192 successful = true; 193 clear (); 194 return *this; 195 } 196 197 bool in_error () const { return !successful; } 198 199 bool alloc (unsigned new_population = 0) 200 { 201 if (unlikely (!successful)) return false; 202 203 if (new_population != 0 && (new_population + new_population / 2) < mask) return true; 204 205 unsigned int power = hb_bit_storage (hb_max (hb_max ((unsigned) population, new_population) * 2, 4u)); 206 unsigned int new_size = 1u << power; 207 item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); 208 if (unlikely (!new_items)) 209 { 210 successful = false; 211 return false; 212 } 213 if (!item_t::is_trivially_constructible) 214 for (auto &_ : hb_iter (new_items, new_size)) 215 new (&_) item_t (); 216 else 217 hb_memset (new_items, 0, (size_t) new_size * sizeof (item_t)); 218 219 unsigned int old_size = size (); 220 item_t *old_items = items; 221 222 /* Switch to new, empty, array. */ 223 population = occupancy = 0; 224 mask = new_size - 1; 225 prime = prime_for (power); 226 max_chain_length = power * 2; 227 items = new_items; 228 229 /* Insert back old items. */ 230 for (unsigned int i = 0; i < old_size; i++) 231 { 232 if (old_items[i].is_real ()) 233 { 234 set_with_hash (std::move (old_items[i].key), 235 old_items[i].hash, 236 std::move (old_items[i].value)); 237 } 238 } 239 for (unsigned int i = 0; i < old_size; i++) 240 old_items[i].~item_t (); 241 242 hb_free (old_items); 243 244 return true; 245 } 246 247 template <typename KK, typename VV> 248 bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool overwrite = true) 249 { 250 if (unlikely (!successful)) return false; 251 if (unlikely ((occupancy + occupancy / 2) >= mask && !alloc ())) return false; 252 253 hash &= 0x3FFFFFFF; // We only store lower 30bit of hash 254 unsigned int tombstone = (unsigned int) -1; 255 unsigned int i = hash % prime; 256 unsigned length = 0; 257 unsigned step = 0; 258 while (items[i].is_used ()) 259 { 260 if ((std::is_integral<K>::value || items[i].hash == hash) && 261 items[i] == key) 262 { 263 if (!overwrite) 264 return false; 265 else 266 break; 267 } 268 if (!items[i].is_real () && tombstone == (unsigned) -1) 269 tombstone = i; 270 i = (i + ++step) & mask; 271 length++; 272 } 273 274 item_t &item = items[tombstone == (unsigned) -1 ? i : tombstone]; 275 276 if (item.is_used ()) 277 { 278 occupancy--; 279 population -= item.is_real (); 280 } 281 282 item.key = std::forward<KK> (key); 283 item.value = std::forward<VV> (value); 284 item.hash = hash; 285 item.set_used (true); 286 item.set_real (true); 287 288 occupancy++; 289 population++; 290 291 if (unlikely (length > max_chain_length) && occupancy * 8 > mask) 292 alloc (mask - 8); // This ensures we jump to next larger size 293 294 return true; 295 } 296 297 template <typename VV> 298 bool set (const K &key, VV&& value, bool overwrite = true) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value), overwrite); } 299 template <typename VV> 300 bool set (K &&key, VV&& value, bool overwrite = true) 301 { 302 uint32_t hash = hb_hash (key); 303 return set_with_hash (std::move (key), hash, std::forward<VV> (value), overwrite); 304 } 305 bool add (const K &key) 306 { 307 uint32_t hash = hb_hash (key); 308 return set_with_hash (key, hash, item_t::default_value ()); 309 } 310 311 const V& get_with_hash (const K &key, uint32_t hash) const 312 { 313 if (!items) return item_t::default_value (); 314 auto *item = fetch_item (key, hash); 315 if (item) 316 return item->value; 317 return item_t::default_value (); 318 } 319 const V& get (const K &key) const 320 { 321 if (!items) return item_t::default_value (); 322 return get_with_hash (key, hb_hash (key)); 323 } 324 325 void del (const K &key) 326 { 327 if (!items) return; 328 auto *item = fetch_item (key, hb_hash (key)); 329 if (item) 330 { 331 item->set_real (false); 332 population--; 333 } 334 } 335 336 /* Has interface. */ 337 const V& operator [] (K k) const { return get (k); } 338 template <typename VV=V> 339 bool has (const K &key, VV **vp = nullptr) const 340 { 341 if (!items) return false; 342 return has_with_hash (key, hb_hash (key), vp); 343 } 344 template <typename VV=V> 345 bool has_with_hash (const K &key, uint32_t hash, VV **vp = nullptr) const 346 { 347 if (!items) return false; 348 auto *item = fetch_item (key, hash); 349 if (item) 350 { 351 if (vp) *vp = std::addressof (item->value); 352 return true; 353 } 354 return false; 355 } 356 item_t *fetch_item (const K &key, uint32_t hash) const 357 { 358 hash &= 0x3FFFFFFF; // We only store lower 30bit of hash 359 unsigned int i = hash % prime; 360 unsigned step = 0; 361 while (items[i].is_used ()) 362 { 363 if ((std::is_integral<K>::value || items[i].hash == hash) && 364 items[i] == key) 365 { 366 if (items[i].is_real ()) 367 return &items[i]; 368 else 369 return nullptr; 370 } 371 i = (i + ++step) & mask; 372 } 373 return nullptr; 374 } 375 /* Projection. */ 376 const V& operator () (K k) const { return get (k); } 377 378 unsigned size () const { return mask ? mask + 1 : 0; } 379 380 void clear () 381 { 382 if (unlikely (!successful)) return; 383 384 for (auto &_ : hb_iter (items, size ())) 385 { 386 /* Reconstruct items. */ 387 _.~item_t (); 388 new (&_) item_t (); 389 } 390 391 population = occupancy = 0; 392 } 393 394 bool is_empty () const { return population == 0; } 395 explicit operator bool () const { return !is_empty (); } 396 397 uint32_t hash () const 398 { 399 return 400 + iter_items () 401 | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) 402 ; 403 } 404 405 bool is_equal (const hb_hashmap_t &other) const 406 { 407 if (population != other.population) return false; 408 409 for (auto pair : iter ()) 410 if (other.get (pair.first) != pair.second) 411 return false; 412 413 return true; 414 } 415 bool operator == (const hb_hashmap_t &other) const { return is_equal (other); } 416 bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); } 417 418 unsigned int get_population () const { return population; } 419 420 void update (const hb_hashmap_t &other) 421 { 422 if (unlikely (!successful)) return; 423 424 hb_copy (other, *this); 425 } 426 427 /* 428 * Iterator 429 */ 430 431 auto iter_items () const HB_AUTO_RETURN 432 ( 433 + hb_iter (items, this->size ()) 434 | hb_filter (&item_t::is_real) 435 ) 436 auto iter_ref () const HB_AUTO_RETURN 437 ( 438 + this->iter_items () 439 | hb_map (&item_t::get_pair_ref) 440 ) 441 auto iter () const HB_AUTO_RETURN 442 ( 443 + this->iter_items () 444 | hb_map (&item_t::get_pair) 445 ) 446 auto keys_ref () const HB_AUTO_RETURN 447 ( 448 + this->iter_items () 449 | hb_map (&item_t::get_key) 450 ) 451 auto keys () const HB_AUTO_RETURN 452 ( 453 + this->keys_ref () 454 | hb_map (hb_ridentity) 455 ) 456 auto values_ref () const HB_AUTO_RETURN 457 ( 458 + this->iter_items () 459 | hb_map (&item_t::get_value) 460 ) 461 auto values () const HB_AUTO_RETURN 462 ( 463 + this->values_ref () 464 | hb_map (hb_ridentity) 465 ) 466 467 /* C iterator. */ 468 bool next (int *idx, 469 K *key, 470 V *value) const 471 { 472 unsigned i = (unsigned) (*idx + 1); 473 474 unsigned count = size (); 475 while (i < count && !items[i].is_real ()) 476 i++; 477 478 if (i >= count) 479 { 480 *idx = -1; 481 return false; 482 } 483 484 *key = items[i].key; 485 *value = items[i].value; 486 487 *idx = (signed) i; 488 return true; 489 } 490 491 /* Sink interface. */ 492 hb_hashmap_t& operator << (const hb_pair_t<K, V>& v) 493 { set (v.first, v.second); return *this; } 494 template <typename V2 = V, 495 hb_enable_if (!std::is_trivially_copyable<V2>::value)> 496 hb_hashmap_t& operator << (const hb_pair_t<K, V&&>& v) 497 { set (v.first, std::move (v.second)); return *this; } 498 template <typename K2 = K, 499 hb_enable_if (!std::is_trivially_copyable<K2>::value)> 500 hb_hashmap_t& operator << (const hb_pair_t<K&&, V>& v) 501 { set (std::move (v.first), v.second); return *this; } 502 template <typename K2 = K, typename V2 = V, 503 hb_enable_if (!std::is_trivially_copyable<K2>::value && 504 !std::is_trivially_copyable<V2>::value)> 505 hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& v) 506 { set (std::move (v.first), std::move (v.second)); return *this; } 507 508 static unsigned int prime_for (unsigned int shift) 509 { 510 /* Following comment and table copied from glib. */ 511 /* Each table size has an associated prime modulo (the first prime 512 * lower than the table size) used to find the initial bucket. Probing 513 * then works modulo 2^n. The prime modulo is necessary to get a 514 * good distribution with poor hash functions. 515 */ 516 /* Not declaring static to make all kinds of compilers happy... */ 517 /*static*/ const unsigned int prime_mod [32] = 518 { 519 1, /* For 1 << 0 */ 520 2, 521 3, 522 7, 523 13, 524 31, 525 61, 526 127, 527 251, 528 509, 529 1021, 530 2039, 531 4093, 532 8191, 533 16381, 534 32749, 535 65521, /* For 1 << 16 */ 536 131071, 537 262139, 538 524287, 539 1048573, 540 2097143, 541 4194301, 542 8388593, 543 16777213, 544 33554393, 545 67108859, 546 134217689, 547 268435399, 548 536870909, 549 1073741789, 550 2147483647 /* For 1 << 31 */ 551 }; 552 553 if (unlikely (shift >= ARRAY_LENGTH (prime_mod))) 554 return prime_mod[ARRAY_LENGTH (prime_mod) - 1]; 555 556 return prime_mod[shift]; 557 } 558 }; 559 560 /* 561 * hb_map_t 562 */ 563 564 struct hb_map_t : hb_hashmap_t<hb_codepoint_t, 565 hb_codepoint_t, 566 true> 567 { 568 using hashmap = hb_hashmap_t<hb_codepoint_t, 569 hb_codepoint_t, 570 true>; 571 572 ~hb_map_t () = default; 573 hb_map_t () : hashmap () {} 574 hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} 575 hb_map_t (hb_map_t &&o) noexcept : hashmap (std::move ((hashmap &) o)) {} 576 hb_map_t& operator= (const hb_map_t&) = default; 577 hb_map_t& operator= (hb_map_t&&) = default; 578 hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {} 579 template <typename Iterable, 580 hb_requires (hb_is_iterable (Iterable))> 581 hb_map_t (const Iterable &o) : hashmap (o) {} 582 }; 583 584 585 #endif /* HB_MAP_HH */