Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/hash.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2021 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 | |
| 25 #include <string.h> | |
| 26 #include <assert.h> | |
| 27 | |
| 28 /* | |
| 29 Simple hashtable with open addressing linear probe. | |
| 30 Unlike text book examples, removing entries works | |
| 31 correctly in this implementation, so it won't start | |
| 32 exhibiting bad behaviour if entries are inserted | |
| 33 and removed frequently. | |
| 34 */ | |
| 35 | |
| 36 typedef struct | |
| 37 { | |
| 38 unsigned char key[FZ_HASH_TABLE_KEY_LENGTH]; | |
| 39 void *val; | |
| 40 } fz_hash_entry; | |
| 41 | |
| 42 struct fz_hash_table | |
| 43 { | |
| 44 int keylen; | |
| 45 int size; | |
| 46 int load; | |
| 47 int lock; /* -1 or the lock used to protect this hash table */ | |
| 48 fz_hash_table_drop_fn *drop_val; | |
| 49 fz_hash_entry *ents; | |
| 50 }; | |
| 51 | |
| 52 static unsigned hash(const unsigned char *s, int len) | |
| 53 { | |
| 54 unsigned val = 0; | |
| 55 int i; | |
| 56 for (i = 0; i < len; i++) | |
| 57 { | |
| 58 val += s[i]; | |
| 59 val += (val << 10); | |
| 60 val ^= (val >> 6); | |
| 61 } | |
| 62 val += (val << 3); | |
| 63 val ^= (val >> 11); | |
| 64 val += (val << 15); | |
| 65 return val; | |
| 66 } | |
| 67 | |
| 68 fz_hash_table * | |
| 69 fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock, fz_hash_table_drop_fn *drop_val) | |
| 70 { | |
| 71 fz_hash_table *table; | |
| 72 | |
| 73 if (keylen > FZ_HASH_TABLE_KEY_LENGTH) | |
| 74 fz_throw(ctx, FZ_ERROR_ARGUMENT, "hash table key length too large"); | |
| 75 | |
| 76 table = fz_malloc_struct(ctx, fz_hash_table); | |
| 77 table->keylen = keylen; | |
| 78 table->size = initialsize; | |
| 79 table->load = 0; | |
| 80 table->lock = lock; | |
| 81 table->drop_val = drop_val; | |
| 82 fz_try(ctx) | |
| 83 { | |
| 84 table->ents = Memento_label(fz_malloc_array(ctx, table->size, fz_hash_entry), "hash_entries"); | |
| 85 memset(table->ents, 0, sizeof(fz_hash_entry) * table->size); | |
| 86 } | |
| 87 fz_catch(ctx) | |
| 88 { | |
| 89 fz_free(ctx, table); | |
| 90 fz_rethrow(ctx); | |
| 91 } | |
| 92 | |
| 93 return table; | |
| 94 } | |
| 95 | |
| 96 void | |
| 97 fz_drop_hash_table(fz_context *ctx, fz_hash_table *table) | |
| 98 { | |
| 99 if (!table) | |
| 100 return; | |
| 101 | |
| 102 if (table->drop_val) | |
| 103 { | |
| 104 int i, n = table->size; | |
| 105 for (i = 0; i < n; ++i) | |
| 106 { | |
| 107 void *v = table->ents[i].val; | |
| 108 if (v) | |
| 109 table->drop_val(ctx, v); | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 fz_free(ctx, table->ents); | |
| 114 fz_free(ctx, table); | |
| 115 } | |
| 116 | |
| 117 static void * | |
| 118 do_hash_insert(fz_context *ctx, fz_hash_table *table, const void *key, void *val) | |
| 119 { | |
| 120 fz_hash_entry *ents; | |
| 121 unsigned size; | |
| 122 unsigned pos; | |
| 123 | |
| 124 ents = table->ents; | |
| 125 size = table->size; | |
| 126 pos = hash(key, table->keylen) % size; | |
| 127 | |
| 128 if (table->lock >= 0) | |
| 129 fz_assert_lock_held(ctx, table->lock); | |
| 130 | |
| 131 while (1) | |
| 132 { | |
| 133 if (!ents[pos].val) | |
| 134 { | |
| 135 memcpy(ents[pos].key, key, table->keylen); | |
| 136 ents[pos].val = val; | |
| 137 table->load ++; | |
| 138 return NULL; | |
| 139 } | |
| 140 | |
| 141 if (memcmp(key, ents[pos].key, table->keylen) == 0) | |
| 142 { | |
| 143 /* This is legal, but should rarely happen. */ | |
| 144 return ents[pos].val; | |
| 145 } | |
| 146 | |
| 147 pos = (pos + 1) % size; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 /* Entered with the lock taken, held throughout and at exit, UNLESS the lock | |
| 152 * is the alloc lock in which case it may be momentarily dropped. */ | |
| 153 static void | |
| 154 fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) | |
| 155 { | |
| 156 fz_hash_entry *oldents = table->ents; | |
| 157 fz_hash_entry *newents; | |
| 158 int oldsize = table->size; | |
| 159 int oldload = table->load; | |
| 160 int i; | |
| 161 | |
| 162 if (newsize < oldload * 8 / 10) | |
| 163 { | |
| 164 fz_warn(ctx, "assert: resize hash too small"); | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 if (table->lock == FZ_LOCK_ALLOC) | |
| 169 fz_unlock(ctx, table->lock); | |
| 170 newents = Memento_label(fz_malloc_no_throw(ctx, newsize * sizeof (fz_hash_entry)), "hash_entries"); | |
| 171 if (table->lock == FZ_LOCK_ALLOC) | |
| 172 fz_lock(ctx, table->lock); | |
| 173 if (table->lock >= 0) | |
| 174 { | |
| 175 if (table->size >= newsize) | |
| 176 { | |
| 177 /* Someone else fixed it before we could lock! */ | |
| 178 if (table->lock == FZ_LOCK_ALLOC) | |
| 179 fz_unlock(ctx, table->lock); | |
| 180 fz_free(ctx, newents); | |
| 181 if (table->lock == FZ_LOCK_ALLOC) | |
| 182 fz_lock(ctx, table->lock); | |
| 183 return; | |
| 184 } | |
| 185 } | |
| 186 if (newents == NULL) | |
| 187 fz_throw(ctx, FZ_ERROR_SYSTEM, "hash table resize failed; out of memory (%d entries)", newsize); | |
| 188 table->ents = newents; | |
| 189 memset(table->ents, 0, sizeof(fz_hash_entry) * newsize); | |
| 190 table->size = newsize; | |
| 191 table->load = 0; | |
| 192 | |
| 193 for (i = 0; i < oldsize; i++) | |
| 194 { | |
| 195 if (oldents[i].val) | |
| 196 { | |
| 197 do_hash_insert(ctx, table, oldents[i].key, oldents[i].val); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 if (table->lock == FZ_LOCK_ALLOC) | |
| 202 fz_unlock(ctx, table->lock); | |
| 203 fz_free(ctx, oldents); | |
| 204 if (table->lock == FZ_LOCK_ALLOC) | |
| 205 fz_lock(ctx, table->lock); | |
| 206 } | |
| 207 | |
| 208 void * | |
| 209 fz_hash_find(fz_context *ctx, fz_hash_table *table, const void *key) | |
| 210 { | |
| 211 fz_hash_entry *ents = table->ents; | |
| 212 unsigned size = table->size; | |
| 213 unsigned pos = hash(key, table->keylen) % size; | |
| 214 | |
| 215 if (table->lock >= 0) | |
| 216 fz_assert_lock_held(ctx, table->lock); | |
| 217 | |
| 218 while (1) | |
| 219 { | |
| 220 if (!ents[pos].val) | |
| 221 return NULL; | |
| 222 | |
| 223 if (memcmp(key, ents[pos].key, table->keylen) == 0) | |
| 224 return ents[pos].val; | |
| 225 | |
| 226 pos = (pos + 1) % size; | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void * | |
| 231 fz_hash_insert(fz_context *ctx, fz_hash_table *table, const void *key, void *val) | |
| 232 { | |
| 233 if (table->load > table->size * 8 / 10) | |
| 234 fz_resize_hash(ctx, table, table->size * 2); | |
| 235 return do_hash_insert(ctx, table, key, val); | |
| 236 } | |
| 237 | |
| 238 static void | |
| 239 do_removal(fz_context *ctx, fz_hash_table *table, unsigned hole) | |
| 240 { | |
| 241 fz_hash_entry *ents = table->ents; | |
| 242 unsigned size = table->size; | |
| 243 unsigned look, code; | |
| 244 | |
| 245 if (table->lock >= 0) | |
| 246 fz_assert_lock_held(ctx, table->lock); | |
| 247 | |
| 248 ents[hole].val = NULL; | |
| 249 | |
| 250 look = hole + 1; | |
| 251 if (look == size) | |
| 252 look = 0; | |
| 253 | |
| 254 while (ents[look].val) | |
| 255 { | |
| 256 code = hash(ents[look].key, table->keylen) % size; | |
| 257 if ((code <= hole && hole < look) || | |
| 258 (look < code && code <= hole) || | |
| 259 (hole < look && look < code)) | |
| 260 { | |
| 261 ents[hole] = ents[look]; | |
| 262 ents[look].val = NULL; | |
| 263 hole = look; | |
| 264 } | |
| 265 | |
| 266 look++; | |
| 267 if (look == size) | |
| 268 look = 0; | |
| 269 } | |
| 270 | |
| 271 table->load --; | |
| 272 } | |
| 273 | |
| 274 void | |
| 275 fz_hash_remove(fz_context *ctx, fz_hash_table *table, const void *key) | |
| 276 { | |
| 277 fz_hash_entry *ents = table->ents; | |
| 278 unsigned size = table->size; | |
| 279 unsigned pos = hash(key, table->keylen) % size; | |
| 280 | |
| 281 if (table->lock >= 0) | |
| 282 fz_assert_lock_held(ctx, table->lock); | |
| 283 | |
| 284 while (1) | |
| 285 { | |
| 286 if (!ents[pos].val) | |
| 287 { | |
| 288 fz_warn(ctx, "assert: remove non-existent hash entry"); | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 if (memcmp(key, ents[pos].key, table->keylen) == 0) | |
| 293 { | |
| 294 do_removal(ctx, table, pos); | |
| 295 return; | |
| 296 } | |
| 297 | |
| 298 pos++; | |
| 299 if (pos == size) | |
| 300 pos = 0; | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 void | |
| 305 fz_hash_for_each(fz_context *ctx, fz_hash_table *table, void *state, fz_hash_table_for_each_fn *callback) | |
| 306 { | |
| 307 int i; | |
| 308 for (i = 0; i < table->size; ++i) | |
| 309 if (table->ents[i].val) | |
| 310 callback(ctx, state, table->ents[i].key, table->keylen, table->ents[i].val); | |
| 311 } | |
| 312 | |
| 313 void | |
| 314 fz_hash_filter(fz_context *ctx, fz_hash_table *table, void *state, fz_hash_table_filter_fn *callback) | |
| 315 { | |
| 316 int i; | |
| 317 restart: | |
| 318 for (i = 0; i < table->size; ++i) | |
| 319 { | |
| 320 if (table->ents[i].val) | |
| 321 { | |
| 322 if (callback(ctx, state, table->ents[i].key, table->keylen, table->ents[i].val)) | |
| 323 { | |
| 324 do_removal(ctx, table, i); | |
| 325 goto restart; /* we may have moved some slots around, so just restart the scan */ | |
| 326 } | |
| 327 } | |
| 328 } | |
| 329 } |
