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
33template <typename T>
34inline uint32_t Hash (const T &v)
35{
36 /* Knuth's multiplicative method: */
37 return (uint32_t) v * 2654435761u;
38}
39
40
41/*
42 * hb_map_t
43 */
44
45struct hb_map_t
46{
47 HB_NO_COPY_ASSIGN (hb_map_t);
48 hb_map_t () { init (); }
49 ~hb_map_t () { fini (); }
50
51 struct item_t
52 {
53 hb_codepoint_t key;
54 hb_codepoint_t value;
55
56 bool is_unused () const { return key == INVALID; }
57 bool is_tombstone () const { return key != INVALID && value == INVALID; }
58 };
59
60 hb_object_header_t header;
61 bool successful; /* Allocations successful */
62 unsigned int population; /* Not including tombstones. */
63 unsigned int occupancy; /* Including tombstones. */
64 unsigned int mask;
65 unsigned int prime;
66 item_t *items;
67
68 void init_shallow ()
69 {
70 successful = true;
71 population = occupancy = 0;
72 mask = 0;
73 prime = 0;
74 items = nullptr;
75 }
76 void init ()
77 {
78 hb_object_init (this);
79 init_shallow ();
80 }
81 void fini_shallow ()
82 {
83 free (items);
84 items = nullptr;
85 }
86 void fini ()
87 {
88 population = occupancy = 0;
89 hb_object_fini (this);
90 fini_shallow ();
91 }
92
93 bool in_error () const { return !successful; }
94
95 bool resize ()
96 {
97 if (unlikely (!successful)) return false;
98
99 unsigned int power = hb_bit_storage (population * 2 + 8);
100 unsigned int new_size = 1u << power;
101 item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t));
102 if (unlikely (!new_items))
103 {
104 successful = false;
105 return false;
106 }
107 memset (new_items, 0xFF, (size_t) new_size * sizeof (item_t));
108
109 unsigned int old_size = mask + 1;
110 item_t *old_items = items;
111
112 /* Switch to new, empty, array. */
113 population = occupancy = 0;
114 mask = new_size - 1;
115 prime = prime_for (power);
116 items = new_items;
117
118 /* Insert back old items. */
119 if (old_items)
120 for (unsigned int i = 0; i < old_size; i++)
121 if (old_items[i].key != INVALID && old_items[i].value != INVALID)
122 set (old_items[i].key, old_items[i].value);
123
124 free (old_items);
125
126 return true;
127 }
128
129 void set (hb_codepoint_t key, hb_codepoint_t value)
130 {
131 if (unlikely (!successful)) return;
132 if (unlikely (key == INVALID)) return;
133 if ((occupancy + occupancy / 2) >= mask && !resize ()) return;
134 unsigned int i = bucket_for (key);
135
136 if (value == INVALID && items[i].key != key)
137 return; /* Trying to delete non-existent key. */
138
139 if (!items[i].is_unused ())
140 {
141 occupancy--;
142 if (items[i].is_tombstone ())
143 population--;
144 }
145
146 items[i].key = key;
147 items[i].value = value;
148
149 occupancy++;
150 if (!items[i].is_tombstone ())
151 population++;
152
153 }
154 hb_codepoint_t get (hb_codepoint_t key) const
155 {
156 if (unlikely (!items)) return INVALID;
157 unsigned int i = bucket_for (key);
158 return items[i].key == key ? items[i].value : INVALID;
159 }
160
161 void del (hb_codepoint_t key) { set (key, INVALID); }
162
163 bool has (hb_codepoint_t key) const
164 { return get (key) != INVALID; }
165
166 hb_codepoint_t operator [] (unsigned int key) const
167 { return get (key); }
168
169 static constexpr hb_codepoint_t INVALID = HB_MAP_VALUE_INVALID;
170
171 void clear ()
172 {
173 memset (items, 0xFF, ((size_t) mask + 1) * sizeof (item_t));
174 population = occupancy = 0;
175 }
176
177 bool is_empty () const { return population == 0; }
178
179 unsigned int get_population () const { return population; }
180
181 protected:
182
183 unsigned int bucket_for (hb_codepoint_t key) const
184 {
185 unsigned int i = Hash (key) % prime;
186 unsigned int step = 0;
187 unsigned int tombstone = INVALID;
188 while (!items[i].is_unused ())
189 {
190 if (items[i].key == key)
191 return i;
192 if (tombstone == INVALID && items[i].is_tombstone ())
193 tombstone = i;
194 i = (i + ++step) & mask;
195 }
196 return tombstone == INVALID ? i : tombstone;
197 }
198
199 static unsigned int prime_for (unsigned int shift)
200 {
201 /* Following comment and table copied from glib. */
202 /* Each table size has an associated prime modulo (the first prime
203 * lower than the table size) used to find the initial bucket. Probing
204 * then works modulo 2^n. The prime modulo is necessary to get a
205 * good distribution with poor hash functions.
206 */
207 /* Not declaring static to make all kinds of compilers happy... */
208 /*static*/ const unsigned int prime_mod [32] =
209 {
210 1, /* For 1 << 0 */
211 2,
212 3,
213 7,
214 13,
215 31,
216 61,
217 127,
218 251,
219 509,
220 1021,
221 2039,
222 4093,
223 8191,
224 16381,
225 32749,
226 65521, /* For 1 << 16 */
227 131071,
228 262139,
229 524287,
230 1048573,
231 2097143,
232 4194301,
233 8388593,
234 16777213,
235 33554393,
236 67108859,
237 134217689,
238 268435399,
239 536870909,
240 1073741789,
241 2147483647 /* For 1 << 31 */
242 };
243
244 if (unlikely (shift >= ARRAY_LENGTH (prime_mod)))
245 return prime_mod[ARRAY_LENGTH (prime_mod) - 1];
246
247 return prime_mod[shift];
248 }
249};
250
251
252#endif /* HB_MAP_HH */
253