1 #include "mupdf/fitz.h"
4 Simple hashtable with open addressing linear probe.
5 Unlike text book examples, removing entries works
6 correctly in this implementation, so it wont start
7 exhibiting bad behaviour if entries are inserted
8 and removed frequently.
11 enum { MAX_KEY_LEN = 48 };
12 typedef struct fz_hash_entry_s fz_hash_entry;
14 struct fz_hash_entry_s
16 unsigned char key[MAX_KEY_LEN];
20 struct fz_hash_table_s
25 int lock; /* -1 or the lock used to protect this hash table */
29 static unsigned hash(const unsigned char *s, int len)
33 for (i = 0; i < len; i++)
46 fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock)
50 assert(keylen <= MAX_KEY_LEN);
52 table = fz_malloc_struct(ctx, fz_hash_table);
53 table->keylen = keylen;
54 table->size = initialsize;
59 table->ents = fz_malloc_array(ctx, table->size, sizeof(fz_hash_entry));
60 memset(table->ents, 0, sizeof(fz_hash_entry) * table->size);
72 fz_empty_hash(fz_context *ctx, fz_hash_table *table)
75 memset(table->ents, 0, sizeof(fz_hash_entry) * table->size);
79 fz_hash_len(fz_context *ctx, fz_hash_table *table)
85 fz_hash_get_key(fz_context *ctx, fz_hash_table *table, int idx)
87 return table->ents[idx].key;
91 fz_hash_get_val(fz_context *ctx, fz_hash_table *table, int idx)
93 return table->ents[idx].val;
97 fz_free_hash(fz_context *ctx, fz_hash_table *table)
99 fz_free(ctx, table->ents);
104 do_hash_insert(fz_context *ctx, fz_hash_table *table, const void *key, void *val, unsigned *pos_ptr)
112 pos = hash(key, table->keylen) % size;
114 if (table->lock >= 0)
115 fz_assert_lock_held(ctx, table->lock);
121 memcpy(ents[pos].key, key, table->keylen);
129 if (memcmp(key, ents[pos].key, table->keylen) == 0)
131 /* This is legal, but should happen rarely in the non
136 fz_warn(ctx, "assert: overwrite hash slot");
137 return ents[pos].val;
140 pos = (pos + 1) % size;
144 /* Entered with the lock taken, held throughout and at exit, UNLESS the lock
145 * is the alloc lock in which case it may be momentarily dropped. */
147 fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize)
149 fz_hash_entry *oldents = table->ents;
150 fz_hash_entry *newents;
151 int oldsize = table->size;
152 int oldload = table->load;
155 if (newsize < oldload * 8 / 10)
157 fz_warn(ctx, "assert: resize hash too small");
161 if (table->lock == FZ_LOCK_ALLOC)
162 fz_unlock(ctx, FZ_LOCK_ALLOC);
163 newents = fz_malloc_array_no_throw(ctx, newsize, sizeof(fz_hash_entry));
164 if (table->lock == FZ_LOCK_ALLOC)
165 fz_lock(ctx, FZ_LOCK_ALLOC);
166 if (table->lock >= 0)
168 if (table->size >= newsize)
170 /* Someone else fixed it before we could lock! */
171 if (table->lock == FZ_LOCK_ALLOC)
172 fz_unlock(ctx, table->lock);
173 fz_free(ctx, newents);
174 if (table->lock == FZ_LOCK_ALLOC)
175 fz_lock(ctx, table->lock);
180 fz_throw(ctx, FZ_ERROR_GENERIC, "hash table resize failed; out of memory (%d entries)", newsize);
181 table->ents = newents;
182 memset(table->ents, 0, sizeof(fz_hash_entry) * newsize);
183 table->size = newsize;
186 for (i = 0; i < oldsize; i++)
190 do_hash_insert(ctx, table, oldents[i].key, oldents[i].val, NULL);
194 if (table->lock == FZ_LOCK_ALLOC)
195 fz_unlock(ctx, FZ_LOCK_ALLOC);
196 fz_free(ctx, oldents);
197 if (table->lock == FZ_LOCK_ALLOC)
198 fz_lock(ctx, FZ_LOCK_ALLOC);
202 fz_hash_find(fz_context *ctx, fz_hash_table *table, const void *key)
204 fz_hash_entry *ents = table->ents;
205 unsigned size = table->size;
206 unsigned pos = hash(key, table->keylen) % size;
208 if (table->lock >= 0)
209 fz_assert_lock_held(ctx, table->lock);
216 if (memcmp(key, ents[pos].key, table->keylen) == 0)
217 return ents[pos].val;
219 pos = (pos + 1) % size;
224 fz_hash_insert(fz_context *ctx, fz_hash_table *table, const void *key, void *val)
226 if (table->load > table->size * 8 / 10)
228 fz_resize_hash(ctx, table, table->size * 2);
231 return do_hash_insert(ctx, table, key, val, NULL);
235 fz_hash_insert_with_pos(fz_context *ctx, fz_hash_table *table, const void *key, void *val, unsigned *pos)
237 if (table->load > table->size * 8 / 10)
239 fz_resize_hash(ctx, table, table->size * 2);
242 return do_hash_insert(ctx, table, key, val, pos);
246 do_removal(fz_context *ctx, fz_hash_table *table, const void *key, unsigned hole)
248 fz_hash_entry *ents = table->ents;
249 unsigned size = table->size;
252 if (table->lock >= 0)
253 fz_assert_lock_held(ctx, table->lock);
255 ents[hole].val = NULL;
261 while (ents[look].val)
263 code = hash(ents[look].key, table->keylen) % size;
264 if ((code <= hole && hole < look) ||
265 (look < code && code <= hole) ||
266 (hole < look && look < code))
268 ents[hole] = ents[look];
269 ents[look].val = NULL;
282 fz_hash_remove(fz_context *ctx, fz_hash_table *table, const void *key)
284 fz_hash_entry *ents = table->ents;
285 unsigned size = table->size;
286 unsigned pos = hash(key, table->keylen) % size;
288 if (table->lock >= 0)
289 fz_assert_lock_held(ctx, table->lock);
295 fz_warn(ctx, "assert: remove non-existent hash entry");
299 if (memcmp(key, ents[pos].key, table->keylen) == 0)
301 do_removal(ctx, table, key, pos);
312 fz_hash_remove_fast(fz_context *ctx, fz_hash_table *table, const void *key, unsigned pos)
314 fz_hash_entry *ents = table->ents;
316 if (ents[pos].val == NULL || memcmp(key, ents[pos].key, table->keylen) != 0)
318 /* The value isn't there, or the key didn't match! The table
319 * must have been rebuilt (or the contents moved) in the
320 * meantime. Do the removal the slow way. */
321 fz_hash_remove(ctx, table, key);
324 do_removal(ctx, table, key, pos);
329 fz_print_hash(fz_context *ctx, FILE *out, fz_hash_table *table)
331 fz_print_hash_details(ctx, out, table, NULL);
335 fz_print_hash_details(fz_context *ctx, FILE *out, fz_hash_table *table, void (*details)(FILE *,void*))
339 fprintf(out, "cache load %d / %d\n", table->load, table->size);
341 for (i = 0; i < table->size; i++)
343 if (!table->ents[i].val)
344 fprintf(out, "table % 4d: empty\n", i);
347 fprintf(out, "table % 4d: key=", i);
348 for (k = 0; k < MAX_KEY_LEN; k++)
349 fprintf(out, "%02x", ((char*)table->ents[i].key)[k]);
351 details(out, table->ents[i].val);
353 fprintf(out, " val=$%p\n", table->ents[i].val);