tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */