cairo-script-hash.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * Copyright © 2004 Red Hat, Inc.
  3. * Copyright © 2005 Red Hat, Inc.
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it either under the terms of the GNU Lesser General Public
  7. * License version 2.1 as published by the Free Software Foundation
  8. * (the "LGPL") or, at your option, under the terms of the Mozilla
  9. * Public License Version 1.1 (the "MPL"). If you do not alter this
  10. * notice, a recipient may use your version of this file under either
  11. * the MPL or the LGPL.
  12. *
  13. * You should have received a copy of the LGPL along with this library
  14. * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  16. * You should have received a copy of the MPL along with this library
  17. * in the file COPYING-MPL-1.1
  18. *
  19. * The contents of this file are subject to the Mozilla Public License
  20. * Version 1.1 (the "License"); you may not use this file except in
  21. * compliance with the License. You may obtain a copy of the License at
  22. * http://www.mozilla.org/MPL/
  23. *
  24. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  25. * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  26. * the specific language governing rights and limitations.
  27. *
  28. * The Original Code is the cairo graphics library.
  29. *
  30. * The Initial Developer of the Original Code is Red Hat, Inc.
  31. *
  32. * Contributor(s):
  33. * Keith Packard <keithp@keithp.com>
  34. * Graydon Hoare <graydon@redhat.com>
  35. * Carl Worth <cworth@cworth.org>
  36. * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
  37. */
  38. #include "config.h"
  39. #include "cairo-script-private.h"
  40. #include <stdlib.h>
  41. /*
  42. * An entry can be in one of three states:
  43. *
  44. * FREE: Entry has never been used, terminates all searches.
  45. * Appears in the table as a %NULL pointer.
  46. *
  47. * DEAD: Entry had been live in the past. A dead entry can be reused
  48. * but does not terminate a search for an exact entry.
  49. * Appears in the table as a pointer to DEAD_ENTRY.
  50. *
  51. * LIVE: Entry is currently being used.
  52. * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer.
  53. */
  54. #define DEAD_ENTRY ((csi_hash_entry_t *) 0x1)
  55. #define ENTRY_IS_FREE(entry) ((entry) == NULL)
  56. #define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY)
  57. #define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY)
  58. /* This table is open-addressed with double hashing. Each table size is a
  59. * prime chosen to be a little more than double the high water mark for a
  60. * given arrangement, so the tables should remain < 50% full. The table
  61. * size makes for the "first" hash modulus; a second prime (2 less than the
  62. * first prime) serves as the "second" hash modulus, which is co-prime and
  63. * thus guarantees a complete permutation of table indices.
  64. *
  65. * This structure, and accompanying table, is borrowed/modified from the
  66. * file xserver/render/glyph.c in the freedesktop.org x server, with
  67. * permission (and suggested modification of doubling sizes) by Keith
  68. * Packard.
  69. */
  70. static const csi_hash_table_arrangement_t hash_table_arrangements [] = {
  71. { 16, 43, 41 },
  72. { 32, 73, 71 },
  73. { 64, 151, 149 },
  74. { 128, 283, 281 },
  75. { 256, 571, 569 },
  76. { 512, 1153, 1151 },
  77. { 1024, 2269, 2267 },
  78. { 2048, 4519, 4517 },
  79. { 4096, 9013, 9011 },
  80. { 8192, 18043, 18041 },
  81. { 16384, 36109, 36107 },
  82. { 32768, 72091, 72089 },
  83. { 65536, 144409, 144407 },
  84. { 131072, 288361, 288359 },
  85. { 262144, 576883, 576881 },
  86. { 524288, 1153459, 1153457 },
  87. { 1048576, 2307163, 2307161 },
  88. { 2097152, 4613893, 4613891 },
  89. { 4194304, 9227641, 9227639 },
  90. { 8388608, 18455029, 18455027 },
  91. { 16777216, 36911011, 36911009 },
  92. { 33554432, 73819861, 73819859 },
  93. { 67108864, 147639589, 147639587 },
  94. { 134217728, 295279081, 295279079 },
  95. { 268435456, 590559793, 590559791 }
  96. };
  97. #define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements)
  98. /**
  99. * _csi_hash_table_create:
  100. * @keys_equal: a function to return %TRUE if two keys are equal
  101. *
  102. * Creates a new hash table which will use the keys_equal() function
  103. * to compare hash keys. Data is provided to the hash table in the
  104. * form of user-derived versions of #csi_hash_entry_t. A hash entry
  105. * must be able to hold both a key (including a hash code) and a
  106. * value. Sometimes only the key will be necessary, (as in
  107. * _csi_hash_table_remove), and other times both a key and a value
  108. * will be necessary, (as in _csi_hash_table_insert).
  109. *
  110. * See #csi_hash_entry_t for more details.
  111. *
  112. * Return value: the new hash table or %NULL if out of memory.
  113. **/
  114. csi_status_t
  115. _csi_hash_table_init (csi_hash_table_t *hash_table,
  116. csi_hash_keys_equal_func_t keys_equal)
  117. {
  118. hash_table->keys_equal = keys_equal;
  119. hash_table->arrangement = &hash_table_arrangements[0];
  120. hash_table->entries = calloc (hash_table->arrangement->size,
  121. sizeof(csi_hash_entry_t *));
  122. if (hash_table->entries == NULL)
  123. return _csi_error (CAIRO_STATUS_NO_MEMORY);
  124. hash_table->live_entries = 0;
  125. hash_table->used_entries = 0;
  126. hash_table->iterating = 0;
  127. return CSI_STATUS_SUCCESS;
  128. }
  129. /**
  130. * _csi_hash_table_destroy:
  131. * @hash_table: an empty hash table to destroy
  132. *
  133. * Immediately destroys the given hash table, freeing all resources
  134. * associated with it.
  135. *
  136. * WARNING: The hash_table must have no live entries in it before
  137. * _csi_hash_table_destroy is called. It is a fatal error otherwise,
  138. * and this function will halt. The rationale for this behavior is to
  139. * avoid memory leaks and to avoid needless complication of the API
  140. * with destroy notifiy callbacks.
  141. *
  142. * WARNING: The hash_table must have no running iterators in it when
  143. * _csi_hash_table_destroy is called. It is a fatal error otherwise,
  144. * and this function will halt.
  145. **/
  146. void
  147. _csi_hash_table_fini (csi_hash_table_t *hash_table)
  148. {
  149. free (hash_table->entries);
  150. }
  151. static csi_hash_entry_t **
  152. _csi_hash_table_lookup_unique_key (csi_hash_table_t *hash_table,
  153. csi_hash_entry_t *key)
  154. {
  155. unsigned long table_size, i, idx, step;
  156. csi_hash_entry_t **entry;
  157. table_size = hash_table->arrangement->size;
  158. idx = key->hash % table_size;
  159. entry = &hash_table->entries[idx];
  160. if (! ENTRY_IS_LIVE (*entry))
  161. return entry;
  162. i = 1;
  163. step = key->hash % hash_table->arrangement->rehash;
  164. if (step == 0)
  165. step = 1;
  166. do {
  167. idx += step;
  168. if (idx >= table_size)
  169. idx -= table_size;
  170. entry = &hash_table->entries[idx];
  171. if (! ENTRY_IS_LIVE (*entry))
  172. return entry;
  173. } while (++i < table_size);
  174. return NULL;
  175. }
  176. /**
  177. * _csi_hash_table_manage:
  178. * @hash_table: a hash table
  179. *
  180. * Resize the hash table if the number of entries has gotten much
  181. * bigger or smaller than the ideal number of entries for the current
  182. * size, or control the number of dead entries by moving the entries
  183. * within the table.
  184. *
  185. * Return value: %CAIRO_STATUS_SUCCESS if successful or
  186. * %CAIRO_STATUS_NO_MEMORY if out of memory.
  187. **/
  188. static csi_status_t
  189. _csi_hash_table_manage (csi_hash_table_t *hash_table)
  190. {
  191. csi_hash_table_t tmp;
  192. csi_boolean_t realloc = TRUE;
  193. unsigned long i;
  194. /* This keeps the size of the hash table between 2 and approximately 8
  195. * times the number of live entries and keeps the proportion of free
  196. * entries (search-terminations) > 25%.
  197. */
  198. unsigned long high = hash_table->arrangement->high_water_mark;
  199. unsigned long low = high >> 2;
  200. unsigned long max_used = high + high / 2;
  201. tmp = *hash_table;
  202. if (hash_table->live_entries > high) {
  203. tmp.arrangement = hash_table->arrangement + 1;
  204. /* This code is being abused if we can't make a table big enough. */
  205. } else if (hash_table->live_entries < low &&
  206. /* Can't shrink if we're at the smallest size */
  207. hash_table->arrangement != &hash_table_arrangements[0])
  208. {
  209. tmp.arrangement = hash_table->arrangement - 1;
  210. }
  211. else if (hash_table->used_entries > max_used)
  212. {
  213. /* Clean out dead entries to prevent lookups from becoming too slow. */
  214. for (i = 0; i < hash_table->arrangement->size; ++i) {
  215. if (ENTRY_IS_DEAD (hash_table->entries[i]))
  216. hash_table->entries[i] = NULL;
  217. }
  218. hash_table->used_entries = hash_table->live_entries;
  219. /* There is no need to reallocate but some entries may need to be
  220. * moved. Typically the proportion of entries needing to be moved is
  221. * small, but, if the moving should leave a large number of dead
  222. * entries, they will be cleaned out next time this code is
  223. * executed. */
  224. realloc = FALSE;
  225. }
  226. else
  227. {
  228. return CAIRO_STATUS_SUCCESS;
  229. }
  230. if (realloc) {
  231. tmp.entries = calloc (tmp.arrangement->size,
  232. sizeof (csi_hash_entry_t*));
  233. if (tmp.entries == NULL)
  234. return _csi_error (CAIRO_STATUS_NO_MEMORY);
  235. hash_table->used_entries = 0;
  236. }
  237. for (i = 0; i < hash_table->arrangement->size; ++i) {
  238. csi_hash_entry_t *entry, **pos;
  239. entry = hash_table->entries[i];
  240. if (ENTRY_IS_LIVE (entry)) {
  241. hash_table->entries[i] = DEAD_ENTRY;
  242. pos = _csi_hash_table_lookup_unique_key (&tmp, entry);
  243. if (ENTRY_IS_FREE (*pos))
  244. hash_table->used_entries++;
  245. *pos = entry;
  246. }
  247. }
  248. if (realloc) {
  249. free (hash_table->entries);
  250. hash_table->entries = tmp.entries;
  251. hash_table->arrangement = tmp.arrangement;
  252. }
  253. return CAIRO_STATUS_SUCCESS;
  254. }
  255. /**
  256. * _csi_hash_table_lookup:
  257. * @hash_table: a hash table
  258. * @key: the key of interest
  259. *
  260. * Performs a lookup in @hash_table looking for an entry which has a
  261. * key that matches @key, (as determined by the keys_equal() function
  262. * passed to _csi_hash_table_create).
  263. *
  264. * Return value: the matching entry, of %NULL if no match was found.
  265. **/
  266. void *
  267. _csi_hash_table_lookup (csi_hash_table_t *hash_table,
  268. csi_hash_entry_t *key)
  269. {
  270. csi_hash_entry_t **entry;
  271. unsigned long table_size, i, idx, step;
  272. table_size = hash_table->arrangement->size;
  273. idx = key->hash % table_size;
  274. entry = &hash_table->entries[idx];
  275. if (ENTRY_IS_LIVE (*entry)) {
  276. if ((*entry)->hash == key->hash && hash_table->keys_equal (key, *entry))
  277. return *entry;
  278. } else if (ENTRY_IS_FREE (*entry))
  279. return NULL;
  280. i = 1;
  281. step = key->hash % hash_table->arrangement->rehash;
  282. if (step == 0)
  283. step = 1;
  284. do {
  285. idx += step;
  286. if (idx >= table_size)
  287. idx -= table_size;
  288. entry = &hash_table->entries[idx];
  289. if (ENTRY_IS_LIVE (*entry)) {
  290. if ((*entry)->hash == key->hash &&
  291. hash_table->keys_equal (key, *entry))
  292. {
  293. return *entry;
  294. }
  295. } else if (ENTRY_IS_FREE (*entry))
  296. return NULL;
  297. } while (++i < table_size);
  298. return NULL;
  299. }
  300. /**
  301. * _csi_hash_table_insert:
  302. * @hash_table: a hash table
  303. * @key_and_value: an entry to be inserted
  304. *
  305. * Insert the entry #key_and_value into the hash table.
  306. *
  307. * WARNING: There must not be an existing entry in the hash table
  308. * with a matching key.
  309. *
  310. * WARNING: It is a fatal error to insert an element while
  311. * an iterator is running
  312. *
  313. * Instead of using insert to replace an entry, consider just editing
  314. * the entry obtained with _csi_hash_table_lookup. Or if absolutely
  315. * necessary, use _csi_hash_table_remove first.
  316. *
  317. * Return value: %CAIRO_STATUS_SUCCESS if successful or
  318. * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
  319. **/
  320. csi_status_t
  321. _csi_hash_table_insert (csi_hash_table_t *hash_table,
  322. csi_hash_entry_t *key_and_value)
  323. {
  324. csi_status_t status;
  325. csi_hash_entry_t **entry;
  326. hash_table->live_entries++;
  327. status = _csi_hash_table_manage (hash_table);
  328. if (_csi_unlikely (status)) {
  329. /* abort the insert... */
  330. hash_table->live_entries--;
  331. return status;
  332. }
  333. entry = _csi_hash_table_lookup_unique_key (hash_table,
  334. key_and_value);
  335. if (ENTRY_IS_FREE (*entry))
  336. hash_table->used_entries++;
  337. *entry = key_and_value;
  338. return CAIRO_STATUS_SUCCESS;
  339. }
  340. static csi_hash_entry_t **
  341. _csi_hash_table_lookup_exact_key (csi_hash_table_t *hash_table,
  342. csi_hash_entry_t *key)
  343. {
  344. unsigned long table_size, i, idx, step;
  345. csi_hash_entry_t **entry;
  346. table_size = hash_table->arrangement->size;
  347. idx = key->hash % table_size;
  348. entry = &hash_table->entries[idx];
  349. if (*entry == key)
  350. return entry;
  351. i = 1;
  352. step = key->hash % hash_table->arrangement->rehash;
  353. if (step == 0)
  354. step = 1;
  355. do {
  356. idx += step;
  357. if (idx >= table_size)
  358. idx -= table_size;
  359. entry = &hash_table->entries[idx];
  360. if (*entry == key)
  361. return entry;
  362. } while (++i < table_size);
  363. return NULL;
  364. }
  365. /**
  366. * _csi_hash_table_remove:
  367. * @hash_table: a hash table
  368. * @key: key of entry to be removed
  369. *
  370. * Remove an entry from the hash table which points to @key.
  371. *
  372. * Return value: %CAIRO_STATUS_SUCCESS if successful or
  373. * %CAIRO_STATUS_NO_MEMORY if out of memory.
  374. **/
  375. void
  376. _csi_hash_table_remove (csi_hash_table_t *hash_table,
  377. csi_hash_entry_t *key)
  378. {
  379. *_csi_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
  380. hash_table->live_entries--;
  381. /* Check for table resize. Don't do this when iterating as this will
  382. * reorder elements of the table and cause the iteration to potentially
  383. * skip some elements. */
  384. if (hash_table->iterating == 0) {
  385. /* This call _can_ fail, but only in failing to allocate new
  386. * memory to shrink the hash table. It does leave the table in a
  387. * consistent state, and we've already succeeded in removing the
  388. * entry, so we don't examine the failure status of this call. */
  389. _csi_hash_table_manage (hash_table);
  390. }
  391. }
  392. /**
  393. * _csi_hash_table_foreach:
  394. * @hash_table: a hash table
  395. * @hash_callback: function to be called for each live entry
  396. * @closure: additional argument to be passed to @hash_callback
  397. *
  398. * Call @hash_callback for each live entry in the hash table, in a
  399. * non-specified order.
  400. *
  401. * Entries in @hash_table may be removed by code executed from @hash_callback.
  402. *
  403. * Entries may not be inserted to @hash_table, nor may @hash_table
  404. * be destroyed by code executed from @hash_callback. The relevant
  405. * functions will halt in these cases.
  406. **/
  407. void
  408. _csi_hash_table_foreach (csi_hash_table_t *hash_table,
  409. csi_hash_callback_func_t hash_callback,
  410. void *closure)
  411. {
  412. unsigned long i;
  413. csi_hash_entry_t *entry;
  414. /* Mark the table for iteration */
  415. ++hash_table->iterating;
  416. for (i = 0; i < hash_table->arrangement->size; i++) {
  417. entry = hash_table->entries[i];
  418. if (ENTRY_IS_LIVE(entry))
  419. hash_callback (entry, closure);
  420. }
  421. /* If some elements were deleted during the iteration,
  422. * the table may need resizing. Just do this every time
  423. * as the check is inexpensive.
  424. */
  425. if (--hash_table->iterating == 0) {
  426. /* Should we fail to shrink the hash table, it is left unaltered,
  427. * and we don't need to propagate the error status. */
  428. _csi_hash_table_manage (hash_table);
  429. }
  430. }