hashtab.c 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006, 2008, 2010 Free Software Foundation, Inc.
  2. *
  3. * This library is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU Lesser General Public
  5. * License as published by the Free Software Foundation; either
  6. * version 2.1 of the License, or (at your option) any later version.
  7. *
  8. * This library is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. * Lesser General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public
  14. * License along with this library; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. # include <config.h>
  19. #endif
  20. #include <stdio.h>
  21. #include "libguile/_scm.h"
  22. #include "libguile/alist.h"
  23. #include "libguile/hash.h"
  24. #include "libguile/eval.h"
  25. #include "libguile/root.h"
  26. #include "libguile/vectors.h"
  27. #include "libguile/ports.h"
  28. #include "libguile/validate.h"
  29. #include "libguile/hashtab.h"
  30. /* NOTES
  31. *
  32. * 1. The current hash table implementation uses weak alist vectors
  33. * (implementation in weaks.c) internally, but we do the scanning
  34. * ourselves (in scan_weak_hashtables) because we need to update the
  35. * hash table structure when items are dropped during GC.
  36. *
  37. * 2. All hash table operations still work on alist vectors.
  38. *
  39. */
  40. /* Hash tables are either vectors of association lists or smobs
  41. * containing such vectors. Currently, the vector version represents
  42. * constant size tables while those wrapped in a smob represents
  43. * resizing tables.
  44. *
  45. * Growing or shrinking, with following rehashing, is triggered when
  46. * the load factor
  47. *
  48. * L = N / S (N: number of items in table, S: bucket vector length)
  49. *
  50. * passes an upper limit of 0.9 or a lower limit of 0.25.
  51. *
  52. * The implementation stores the upper and lower number of items which
  53. * trigger a resize in the hashtable object.
  54. *
  55. * Possible hash table sizes (primes) are stored in the array
  56. * hashtable_size.
  57. */
  58. scm_t_bits scm_tc16_hashtable;
  59. static unsigned long hashtable_size[] = {
  60. 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
  61. 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041
  62. #if 0
  63. /* vectors are currently restricted to 2^24-1 = 16777215 elements. */
  64. 28762081, 57524111, 115048217, 230096423, 460192829
  65. /* larger values can't be represented as INUMs */
  66. #endif
  67. };
  68. #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
  69. static char *s_hashtable = "hashtable";
  70. SCM weak_hashtables = SCM_EOL;
  71. static SCM
  72. make_hash_table (int flags, unsigned long k, const char *func_name)
  73. {
  74. SCM table, vector;
  75. scm_t_hashtable *t;
  76. int i = 0, n = k ? k : 31;
  77. while (i < HASHTABLE_SIZE_N && n > hashtable_size[i])
  78. ++i;
  79. n = hashtable_size[i];
  80. if (flags)
  81. vector = scm_i_allocate_weak_vector (flags, scm_from_int (n), SCM_EOL);
  82. else
  83. vector = scm_c_make_vector (n, SCM_EOL);
  84. t = scm_gc_malloc (sizeof (*t), s_hashtable);
  85. t->min_size_index = t->size_index = i;
  86. t->n_items = 0;
  87. t->lower = 0;
  88. t->upper = 9 * n / 10;
  89. t->flags = flags;
  90. t->hash_fn = NULL;
  91. if (flags)
  92. {
  93. SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, weak_hashtables);
  94. weak_hashtables = table;
  95. }
  96. else
  97. SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, SCM_EOL);
  98. return table;
  99. }
  100. void
  101. scm_i_rehash (SCM table,
  102. unsigned long (*hash_fn)(),
  103. void *closure,
  104. const char* func_name)
  105. {
  106. SCM buckets, new_buckets;
  107. int i;
  108. unsigned long old_size;
  109. unsigned long new_size;
  110. if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
  111. {
  112. /* rehashing is not triggered when i <= min_size */
  113. i = SCM_HASHTABLE (table)->size_index;
  114. do
  115. --i;
  116. while (i > SCM_HASHTABLE (table)->min_size_index
  117. && SCM_HASHTABLE_N_ITEMS (table) < hashtable_size[i] / 4);
  118. }
  119. else
  120. {
  121. i = SCM_HASHTABLE (table)->size_index + 1;
  122. if (i >= HASHTABLE_SIZE_N)
  123. /* don't rehash */
  124. return;
  125. /* Remember HASH_FN for rehash_after_gc, but only when CLOSURE
  126. is not needed since CLOSURE can not be guaranteed to be valid
  127. after this function returns.
  128. */
  129. if (closure == NULL)
  130. SCM_HASHTABLE (table)->hash_fn = hash_fn;
  131. }
  132. SCM_HASHTABLE (table)->size_index = i;
  133. new_size = hashtable_size[i];
  134. if (i <= SCM_HASHTABLE (table)->min_size_index)
  135. SCM_HASHTABLE (table)->lower = 0;
  136. else
  137. SCM_HASHTABLE (table)->lower = new_size / 4;
  138. SCM_HASHTABLE (table)->upper = 9 * new_size / 10;
  139. buckets = SCM_HASHTABLE_VECTOR (table);
  140. if (SCM_HASHTABLE_WEAK_P (table))
  141. new_buckets = scm_i_allocate_weak_vector (SCM_HASHTABLE_FLAGS (table),
  142. scm_from_ulong (new_size),
  143. SCM_EOL);
  144. else
  145. new_buckets = scm_c_make_vector (new_size, SCM_EOL);
  146. /* When this is a weak hashtable, running the GC might change it.
  147. We need to cope with this while rehashing its elements. We do
  148. this by first installing the new, empty bucket vector. Then we
  149. remove the elements from the old bucket vector and insert them
  150. into the new one.
  151. */
  152. SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
  153. SCM_SET_HASHTABLE_N_ITEMS (table, 0);
  154. old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
  155. for (i = 0; i < old_size; ++i)
  156. {
  157. SCM ls, cell, handle;
  158. ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
  159. SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
  160. while (scm_is_pair (ls))
  161. {
  162. unsigned long h;
  163. cell = ls;
  164. handle = SCM_CAR (cell);
  165. ls = SCM_CDR (ls);
  166. h = hash_fn (SCM_CAR (handle), new_size, closure);
  167. if (h >= new_size)
  168. scm_out_of_range (func_name, scm_from_ulong (h));
  169. SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h));
  170. SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell);
  171. SCM_HASHTABLE_INCREMENT (table);
  172. }
  173. }
  174. }
  175. static int
  176. hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
  177. {
  178. scm_puts ("#<", port);
  179. if (SCM_HASHTABLE_WEAK_KEY_P (exp))
  180. scm_puts ("weak-key-", port);
  181. else if (SCM_HASHTABLE_WEAK_VALUE_P (exp))
  182. scm_puts ("weak-value-", port);
  183. else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
  184. scm_puts ("doubly-weak-", port);
  185. scm_puts ("hash-table ", port);
  186. scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
  187. scm_putc ('/', port);
  188. scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
  189. 10, port);
  190. scm_puts (">", port);
  191. return 1;
  192. }
  193. #define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
  194. /* keep track of hash tables that need to shrink after scan */
  195. static SCM to_rehash = SCM_EOL;
  196. /* scan hash tables and update hash tables item count */
  197. void
  198. scm_i_scan_weak_hashtables ()
  199. {
  200. SCM *next = &weak_hashtables;
  201. SCM h = *next;
  202. while (!scm_is_null (h))
  203. {
  204. if (!SCM_GC_MARK_P (h))
  205. *next = h = SCM_HASHTABLE_NEXT (h);
  206. else
  207. {
  208. SCM vec = SCM_HASHTABLE_VECTOR (h);
  209. size_t delta = SCM_I_WVECT_DELTA (vec);
  210. SCM_I_SET_WVECT_DELTA (vec, 0);
  211. SCM_SET_HASHTABLE_N_ITEMS (h, SCM_HASHTABLE_N_ITEMS (h) - delta);
  212. if (SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
  213. {
  214. SCM tmp = SCM_HASHTABLE_NEXT (h);
  215. /* temporarily move table from weak_hashtables to to_rehash */
  216. SCM_SET_HASHTABLE_NEXT (h, to_rehash);
  217. to_rehash = h;
  218. *next = h = tmp;
  219. }
  220. else
  221. {
  222. next = SCM_HASHTABLE_NEXTLOC (h);
  223. h = SCM_HASHTABLE_NEXT (h);
  224. }
  225. }
  226. }
  227. }
  228. static void *
  229. rehash_after_gc (void *dummy1 SCM_UNUSED,
  230. void *dummy2 SCM_UNUSED,
  231. void *dummy3 SCM_UNUSED)
  232. {
  233. if (!scm_is_null (to_rehash))
  234. {
  235. SCM first = to_rehash, last, h;
  236. /* important to clear to_rehash here so that we don't get stuck
  237. in an infinite loop if scm_i_rehash causes GC */
  238. to_rehash = SCM_EOL;
  239. h = first;
  240. do
  241. {
  242. /* Rehash only when we have a hash_fn.
  243. */
  244. if (SCM_HASHTABLE (h)->hash_fn)
  245. scm_i_rehash (h, SCM_HASHTABLE (h)->hash_fn, NULL,
  246. "rehash_after_gc");
  247. last = h;
  248. h = SCM_HASHTABLE_NEXT (h);
  249. } while (!scm_is_null (h));
  250. /* move tables back to weak_hashtables */
  251. SCM_SET_HASHTABLE_NEXT (last, weak_hashtables);
  252. weak_hashtables = first;
  253. }
  254. return 0;
  255. }
  256. static size_t
  257. hashtable_free (SCM obj)
  258. {
  259. scm_gc_free (SCM_HASHTABLE (obj), sizeof (scm_t_hashtable), s_hashtable);
  260. return 0;
  261. }
  262. SCM
  263. scm_c_make_hash_table (unsigned long k)
  264. {
  265. return make_hash_table (0, k, "scm_c_make_hash_table");
  266. }
  267. SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
  268. (SCM n),
  269. "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
  270. #define FUNC_NAME s_scm_make_hash_table
  271. {
  272. if (SCM_UNBNDP (n))
  273. return make_hash_table (0, 0, FUNC_NAME);
  274. else
  275. return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
  276. }
  277. #undef FUNC_NAME
  278. SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
  279. (SCM n),
  280. "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
  281. "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
  282. "Return a weak hash table with @var{size} buckets.\n"
  283. "\n"
  284. "You can modify weak hash tables in exactly the same way you\n"
  285. "would modify regular hash tables. (@pxref{Hash Tables})")
  286. #define FUNC_NAME s_scm_make_weak_key_hash_table
  287. {
  288. if (SCM_UNBNDP (n))
  289. return make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
  290. else
  291. return make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
  292. scm_to_ulong (n), FUNC_NAME);
  293. }
  294. #undef FUNC_NAME
  295. SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
  296. (SCM n),
  297. "Return a hash table with weak values with @var{size} buckets.\n"
  298. "(@pxref{Hash Tables})")
  299. #define FUNC_NAME s_scm_make_weak_value_hash_table
  300. {
  301. if (SCM_UNBNDP (n))
  302. return make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
  303. else
  304. {
  305. return make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
  306. scm_to_ulong (n), FUNC_NAME);
  307. }
  308. }
  309. #undef FUNC_NAME
  310. SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
  311. (SCM n),
  312. "Return a hash table with weak keys and values with @var{size}\n"
  313. "buckets. (@pxref{Hash Tables})")
  314. #define FUNC_NAME s_scm_make_doubly_weak_hash_table
  315. {
  316. if (SCM_UNBNDP (n))
  317. return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
  318. 0,
  319. FUNC_NAME);
  320. else
  321. {
  322. return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
  323. scm_to_ulong (n),
  324. FUNC_NAME);
  325. }
  326. }
  327. #undef FUNC_NAME
  328. SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
  329. (SCM obj),
  330. "Return @code{#t} if @var{obj} is an abstract hash table object.")
  331. #define FUNC_NAME s_scm_hash_table_p
  332. {
  333. return scm_from_bool (SCM_HASHTABLE_P (obj));
  334. }
  335. #undef FUNC_NAME
  336. SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
  337. (SCM obj),
  338. "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
  339. "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
  340. "Return @code{#t} if @var{obj} is the specified weak hash\n"
  341. "table. Note that a doubly weak hash table is neither a weak key\n"
  342. "nor a weak value hash table.")
  343. #define FUNC_NAME s_scm_weak_key_hash_table_p
  344. {
  345. return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
  346. }
  347. #undef FUNC_NAME
  348. SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
  349. (SCM obj),
  350. "Return @code{#t} if @var{obj} is a weak value hash table.")
  351. #define FUNC_NAME s_scm_weak_value_hash_table_p
  352. {
  353. return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
  354. }
  355. #undef FUNC_NAME
  356. SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
  357. (SCM obj),
  358. "Return @code{#t} if @var{obj} is a doubly weak hash table.")
  359. #define FUNC_NAME s_scm_doubly_weak_hash_table_p
  360. {
  361. return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
  362. }
  363. #undef FUNC_NAME
  364. SCM
  365. scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure)
  366. #define FUNC_NAME "scm_hash_fn_get_handle"
  367. {
  368. unsigned long k;
  369. SCM h;
  370. if (SCM_HASHTABLE_P (table))
  371. table = SCM_HASHTABLE_VECTOR (table);
  372. else
  373. SCM_VALIDATE_VECTOR (1, table);
  374. if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
  375. return SCM_BOOL_F;
  376. k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (table), closure);
  377. if (k >= SCM_SIMPLE_VECTOR_LENGTH (table))
  378. scm_out_of_range ("hash_fn_get_handle", scm_from_ulong (k));
  379. h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (table, k), closure);
  380. return h;
  381. }
  382. #undef FUNC_NAME
  383. SCM
  384. scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn)(),
  385. SCM (*assoc_fn)(), void * closure)
  386. #define FUNC_NAME "scm_hash_fn_create_handle_x"
  387. {
  388. unsigned long k;
  389. SCM buckets, it;
  390. if (SCM_HASHTABLE_P (table))
  391. buckets = SCM_HASHTABLE_VECTOR (table);
  392. else
  393. {
  394. SCM_ASSERT (scm_is_simple_vector (table),
  395. table, SCM_ARG1, "hash_fn_create_handle_x");
  396. buckets = table;
  397. }
  398. if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
  399. SCM_MISC_ERROR ("void hashtable", SCM_EOL);
  400. k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
  401. if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
  402. scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
  403. it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
  404. if (scm_is_pair (it))
  405. return it;
  406. else if (scm_is_true (it))
  407. scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
  408. else
  409. {
  410. /* When this is a weak hashtable, running the GC can change it.
  411. Thus, we must allocate the new cells first and can only then
  412. access BUCKETS. Also, we need to fetch the bucket vector
  413. again since the hashtable might have been rehashed. This
  414. necessitates a new hash value as well.
  415. */
  416. SCM new_bucket = scm_acons (obj, init, SCM_EOL);
  417. if (!scm_is_eq (table, buckets)
  418. && !scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
  419. {
  420. buckets = SCM_HASHTABLE_VECTOR (table);
  421. k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
  422. if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
  423. scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
  424. }
  425. SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
  426. SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
  427. if (!scm_is_eq (table, buckets))
  428. {
  429. /* Update element count and maybe rehash the table. The
  430. table might have too few entries here since weak hash
  431. tables used with the hashx_* functions can not be
  432. rehashed after GC.
  433. */
  434. SCM_HASHTABLE_INCREMENT (table);
  435. if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
  436. || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
  437. scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
  438. }
  439. return SCM_CAR (new_bucket);
  440. }
  441. }
  442. #undef FUNC_NAME
  443. SCM
  444. scm_hash_fn_ref (SCM table, SCM obj, SCM dflt, unsigned long (*hash_fn)(),
  445. SCM (*assoc_fn)(), void * closure)
  446. {
  447. SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
  448. if (scm_is_pair (it))
  449. return SCM_CDR (it);
  450. else
  451. return dflt;
  452. }
  453. SCM
  454. scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
  455. SCM (*assoc_fn)(), void * closure)
  456. {
  457. SCM it;
  458. it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn, closure);
  459. SCM_SETCDR (it, val);
  460. return val;
  461. }
  462. SCM
  463. scm_hash_fn_remove_x (SCM table, SCM obj,
  464. unsigned long (*hash_fn)(),
  465. SCM (*assoc_fn)(),
  466. void *closure)
  467. {
  468. unsigned long k;
  469. SCM buckets, h;
  470. if (SCM_HASHTABLE_P (table))
  471. buckets = SCM_HASHTABLE_VECTOR (table);
  472. else
  473. {
  474. SCM_ASSERT (scm_is_simple_vector (table), table,
  475. SCM_ARG1, "hash_fn_remove_x");
  476. buckets = table;
  477. }
  478. if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
  479. return SCM_EOL;
  480. k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
  481. if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
  482. scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
  483. h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
  484. if (scm_is_true (h))
  485. {
  486. SCM_SIMPLE_VECTOR_SET
  487. (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
  488. if (!scm_is_eq (table, buckets))
  489. {
  490. SCM_HASHTABLE_DECREMENT (table);
  491. if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
  492. scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
  493. }
  494. }
  495. return h;
  496. }
  497. SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
  498. (SCM table),
  499. "Remove all items from @var{table} (without triggering a resize).")
  500. #define FUNC_NAME s_scm_hash_clear_x
  501. {
  502. if (SCM_HASHTABLE_P (table))
  503. {
  504. scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
  505. SCM_SET_HASHTABLE_N_ITEMS (table, 0);
  506. }
  507. else
  508. scm_vector_fill_x (table, SCM_EOL);
  509. return SCM_UNSPECIFIED;
  510. }
  511. #undef FUNC_NAME
  512. SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
  513. (SCM table, SCM key),
  514. "This procedure returns the @code{(key . value)} pair from the\n"
  515. "hash table @var{table}. If @var{table} does not hold an\n"
  516. "associated value for @var{key}, @code{#f} is returned.\n"
  517. "Uses @code{eq?} for equality testing.")
  518. #define FUNC_NAME s_scm_hashq_get_handle
  519. {
  520. return scm_hash_fn_get_handle (table, key, scm_ihashq, scm_sloppy_assq, 0);
  521. }
  522. #undef FUNC_NAME
  523. SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
  524. (SCM table, SCM key, SCM init),
  525. "This function looks up @var{key} in @var{table} and returns its handle.\n"
  526. "If @var{key} is not already present, a new handle is created which\n"
  527. "associates @var{key} with @var{init}.")
  528. #define FUNC_NAME s_scm_hashq_create_handle_x
  529. {
  530. return scm_hash_fn_create_handle_x (table, key, init, scm_ihashq, scm_sloppy_assq, 0);
  531. }
  532. #undef FUNC_NAME
  533. SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
  534. (SCM table, SCM key, SCM dflt),
  535. "Look up @var{key} in the hash table @var{table}, and return the\n"
  536. "value (if any) associated with it. If @var{key} is not found,\n"
  537. "return @var{default} (or @code{#f} if no @var{default} argument\n"
  538. "is supplied). Uses @code{eq?} for equality testing.")
  539. #define FUNC_NAME s_scm_hashq_ref
  540. {
  541. if (SCM_UNBNDP (dflt))
  542. dflt = SCM_BOOL_F;
  543. return scm_hash_fn_ref (table, key, dflt, scm_ihashq, scm_sloppy_assq, 0);
  544. }
  545. #undef FUNC_NAME
  546. SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
  547. (SCM table, SCM key, SCM val),
  548. "Find the entry in @var{table} associated with @var{key}, and\n"
  549. "store @var{value} there. Uses @code{eq?} for equality testing.")
  550. #define FUNC_NAME s_scm_hashq_set_x
  551. {
  552. return scm_hash_fn_set_x (table, key, val, scm_ihashq, scm_sloppy_assq, 0);
  553. }
  554. #undef FUNC_NAME
  555. SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
  556. (SCM table, SCM key),
  557. "Remove @var{key} (and any value associated with it) from\n"
  558. "@var{table}. Uses @code{eq?} for equality tests.")
  559. #define FUNC_NAME s_scm_hashq_remove_x
  560. {
  561. return scm_hash_fn_remove_x (table, key, scm_ihashq, scm_sloppy_assq, 0);
  562. }
  563. #undef FUNC_NAME
  564. SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
  565. (SCM table, SCM key),
  566. "This procedure returns the @code{(key . value)} pair from the\n"
  567. "hash table @var{table}. If @var{table} does not hold an\n"
  568. "associated value for @var{key}, @code{#f} is returned.\n"
  569. "Uses @code{eqv?} for equality testing.")
  570. #define FUNC_NAME s_scm_hashv_get_handle
  571. {
  572. return scm_hash_fn_get_handle (table, key, scm_ihashv, scm_sloppy_assv, 0);
  573. }
  574. #undef FUNC_NAME
  575. SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
  576. (SCM table, SCM key, SCM init),
  577. "This function looks up @var{key} in @var{table} and returns its handle.\n"
  578. "If @var{key} is not already present, a new handle is created which\n"
  579. "associates @var{key} with @var{init}.")
  580. #define FUNC_NAME s_scm_hashv_create_handle_x
  581. {
  582. return scm_hash_fn_create_handle_x (table, key, init, scm_ihashv,
  583. scm_sloppy_assv, 0);
  584. }
  585. #undef FUNC_NAME
  586. SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
  587. (SCM table, SCM key, SCM dflt),
  588. "Look up @var{key} in the hash table @var{table}, and return the\n"
  589. "value (if any) associated with it. If @var{key} is not found,\n"
  590. "return @var{default} (or @code{#f} if no @var{default} argument\n"
  591. "is supplied). Uses @code{eqv?} for equality testing.")
  592. #define FUNC_NAME s_scm_hashv_ref
  593. {
  594. if (SCM_UNBNDP (dflt))
  595. dflt = SCM_BOOL_F;
  596. return scm_hash_fn_ref (table, key, dflt, scm_ihashv, scm_sloppy_assv, 0);
  597. }
  598. #undef FUNC_NAME
  599. SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
  600. (SCM table, SCM key, SCM val),
  601. "Find the entry in @var{table} associated with @var{key}, and\n"
  602. "store @var{value} there. Uses @code{eqv?} for equality testing.")
  603. #define FUNC_NAME s_scm_hashv_set_x
  604. {
  605. return scm_hash_fn_set_x (table, key, val, scm_ihashv, scm_sloppy_assv, 0);
  606. }
  607. #undef FUNC_NAME
  608. SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
  609. (SCM table, SCM key),
  610. "Remove @var{key} (and any value associated with it) from\n"
  611. "@var{table}. Uses @code{eqv?} for equality tests.")
  612. #define FUNC_NAME s_scm_hashv_remove_x
  613. {
  614. return scm_hash_fn_remove_x (table, key, scm_ihashv, scm_sloppy_assv, 0);
  615. }
  616. #undef FUNC_NAME
  617. SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
  618. (SCM table, SCM key),
  619. "This procedure returns the @code{(key . value)} pair from the\n"
  620. "hash table @var{table}. If @var{table} does not hold an\n"
  621. "associated value for @var{key}, @code{#f} is returned.\n"
  622. "Uses @code{equal?} for equality testing.")
  623. #define FUNC_NAME s_scm_hash_get_handle
  624. {
  625. return scm_hash_fn_get_handle (table, key, scm_ihash, scm_sloppy_assoc, 0);
  626. }
  627. #undef FUNC_NAME
  628. SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
  629. (SCM table, SCM key, SCM init),
  630. "This function looks up @var{key} in @var{table} and returns its handle.\n"
  631. "If @var{key} is not already present, a new handle is created which\n"
  632. "associates @var{key} with @var{init}.")
  633. #define FUNC_NAME s_scm_hash_create_handle_x
  634. {
  635. return scm_hash_fn_create_handle_x (table, key, init, scm_ihash, scm_sloppy_assoc, 0);
  636. }
  637. #undef FUNC_NAME
  638. SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
  639. (SCM table, SCM key, SCM dflt),
  640. "Look up @var{key} in the hash table @var{table}, and return the\n"
  641. "value (if any) associated with it. If @var{key} is not found,\n"
  642. "return @var{default} (or @code{#f} if no @var{default} argument\n"
  643. "is supplied). Uses @code{equal?} for equality testing.")
  644. #define FUNC_NAME s_scm_hash_ref
  645. {
  646. if (SCM_UNBNDP (dflt))
  647. dflt = SCM_BOOL_F;
  648. return scm_hash_fn_ref (table, key, dflt, scm_ihash, scm_sloppy_assoc, 0);
  649. }
  650. #undef FUNC_NAME
  651. SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
  652. (SCM table, SCM key, SCM val),
  653. "Find the entry in @var{table} associated with @var{key}, and\n"
  654. "store @var{value} there. Uses @code{equal?} for equality\n"
  655. "testing.")
  656. #define FUNC_NAME s_scm_hash_set_x
  657. {
  658. return scm_hash_fn_set_x (table, key, val, scm_ihash, scm_sloppy_assoc, 0);
  659. }
  660. #undef FUNC_NAME
  661. SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
  662. (SCM table, SCM key),
  663. "Remove @var{key} (and any value associated with it) from\n"
  664. "@var{table}. Uses @code{equal?} for equality tests.")
  665. #define FUNC_NAME s_scm_hash_remove_x
  666. {
  667. return scm_hash_fn_remove_x (table, key, scm_ihash, scm_sloppy_assoc, 0);
  668. }
  669. #undef FUNC_NAME
  670. typedef struct scm_t_ihashx_closure
  671. {
  672. SCM hash;
  673. SCM assoc;
  674. } scm_t_ihashx_closure;
  675. static unsigned long
  676. scm_ihashx (SCM obj, unsigned long n, scm_t_ihashx_closure *closure)
  677. {
  678. SCM answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
  679. return scm_to_ulong (answer);
  680. }
  681. static SCM
  682. scm_sloppy_assx (SCM obj, SCM alist, scm_t_ihashx_closure *closure)
  683. {
  684. return scm_call_2 (closure->assoc, obj, alist);
  685. }
  686. SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
  687. (SCM hash, SCM assoc, SCM table, SCM key),
  688. "This behaves the same way as the corresponding\n"
  689. "@code{-get-handle} function, but uses @var{hash} as a hash\n"
  690. "function and @var{assoc} to compare keys. @code{hash} must be\n"
  691. "a function that takes two arguments, a key to be hashed and a\n"
  692. "table size. @code{assoc} must be an associator function, like\n"
  693. "@code{assoc}, @code{assq} or @code{assv}.")
  694. #define FUNC_NAME s_scm_hashx_get_handle
  695. {
  696. scm_t_ihashx_closure closure;
  697. closure.hash = hash;
  698. closure.assoc = assoc;
  699. return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
  700. (void *) &closure);
  701. }
  702. #undef FUNC_NAME
  703. SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
  704. (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
  705. "This behaves the same way as the corresponding\n"
  706. "@code{-create-handle} function, but uses @var{hash} as a hash\n"
  707. "function and @var{assoc} to compare keys. @code{hash} must be\n"
  708. "a function that takes two arguments, a key to be hashed and a\n"
  709. "table size. @code{assoc} must be an associator function, like\n"
  710. "@code{assoc}, @code{assq} or @code{assv}.")
  711. #define FUNC_NAME s_scm_hashx_create_handle_x
  712. {
  713. scm_t_ihashx_closure closure;
  714. closure.hash = hash;
  715. closure.assoc = assoc;
  716. return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
  717. scm_sloppy_assx, (void *)&closure);
  718. }
  719. #undef FUNC_NAME
  720. SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
  721. (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
  722. "This behaves the same way as the corresponding @code{ref}\n"
  723. "function, but uses @var{hash} as a hash function and\n"
  724. "@var{assoc} to compare keys. @code{hash} must be a function\n"
  725. "that takes two arguments, a key to be hashed and a table size.\n"
  726. "@code{assoc} must be an associator function, like @code{assoc},\n"
  727. "@code{assq} or @code{assv}.\n"
  728. "\n"
  729. "By way of illustration, @code{hashq-ref table key} is\n"
  730. "equivalent to @code{hashx-ref hashq assq table key}.")
  731. #define FUNC_NAME s_scm_hashx_ref
  732. {
  733. scm_t_ihashx_closure closure;
  734. if (SCM_UNBNDP (dflt))
  735. dflt = SCM_BOOL_F;
  736. closure.hash = hash;
  737. closure.assoc = assoc;
  738. return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
  739. (void *)&closure);
  740. }
  741. #undef FUNC_NAME
  742. SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
  743. (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
  744. "This behaves the same way as the corresponding @code{set!}\n"
  745. "function, but uses @var{hash} as a hash function and\n"
  746. "@var{assoc} to compare keys. @code{hash} must be a function\n"
  747. "that takes two arguments, a key to be hashed and a table size.\n"
  748. "@code{assoc} must be an associator function, like @code{assoc},\n"
  749. "@code{assq} or @code{assv}.\n"
  750. "\n"
  751. " By way of illustration, @code{hashq-set! table key} is\n"
  752. "equivalent to @code{hashx-set! hashq assq table key}.")
  753. #define FUNC_NAME s_scm_hashx_set_x
  754. {
  755. scm_t_ihashx_closure closure;
  756. closure.hash = hash;
  757. closure.assoc = assoc;
  758. return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
  759. (void *)&closure);
  760. }
  761. #undef FUNC_NAME
  762. SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
  763. (SCM hash, SCM assoc, SCM table, SCM obj),
  764. "This behaves the same way as the corresponding @code{remove!}\n"
  765. "function, but uses @var{hash} as a hash function and\n"
  766. "@var{assoc} to compare keys. @code{hash} must be a function\n"
  767. "that takes two arguments, a key to be hashed and a table size.\n"
  768. "@code{assoc} must be an associator function, like @code{assoc},\n"
  769. "@code{assq} or @code{assv}.\n"
  770. "\n"
  771. " By way of illustration, @code{hashq-remove! table key} is\n"
  772. "equivalent to @code{hashx-remove! hashq assq #f table key}.")
  773. #define FUNC_NAME s_scm_hashx_remove_x
  774. {
  775. scm_t_ihashx_closure closure;
  776. closure.hash = hash;
  777. closure.assoc = assoc;
  778. return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
  779. (void *) &closure);
  780. }
  781. #undef FUNC_NAME
  782. /* Hash table iterators */
  783. SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
  784. (SCM proc, SCM init, SCM table),
  785. "An iterator over hash-table elements.\n"
  786. "Accumulates and returns a result by applying PROC successively.\n"
  787. "The arguments to PROC are \"(key value prior-result)\" where key\n"
  788. "and value are successive pairs from the hash table TABLE, and\n"
  789. "prior-result is either INIT (for the first application of PROC)\n"
  790. "or the return value of the previous application of PROC.\n"
  791. "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
  792. "table into an a-list of key-value pairs.")
  793. #define FUNC_NAME s_scm_hash_fold
  794. {
  795. SCM_VALIDATE_PROC (1, proc);
  796. if (!SCM_HASHTABLE_P (table))
  797. SCM_VALIDATE_VECTOR (3, table);
  798. return scm_internal_hash_fold (scm_call_3, (void *) SCM_UNPACK (proc), init, table);
  799. }
  800. #undef FUNC_NAME
  801. static SCM
  802. for_each_proc (void *proc, SCM handle)
  803. {
  804. return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
  805. }
  806. SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
  807. (SCM proc, SCM table),
  808. "An iterator over hash-table elements.\n"
  809. "Applies PROC successively on all hash table items.\n"
  810. "The arguments to PROC are \"(key value)\" where key\n"
  811. "and value are successive pairs from the hash table TABLE.")
  812. #define FUNC_NAME s_scm_hash_for_each
  813. {
  814. SCM_VALIDATE_PROC (1, proc);
  815. if (!SCM_HASHTABLE_P (table))
  816. SCM_VALIDATE_VECTOR (2, table);
  817. scm_internal_hash_for_each_handle (for_each_proc,
  818. (void *) SCM_UNPACK (proc),
  819. table);
  820. return SCM_UNSPECIFIED;
  821. }
  822. #undef FUNC_NAME
  823. SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
  824. (SCM proc, SCM table),
  825. "An iterator over hash-table elements.\n"
  826. "Applies PROC successively on all hash table handles.")
  827. #define FUNC_NAME s_scm_hash_for_each_handle
  828. {
  829. scm_t_trampoline_1 call = scm_trampoline_1 (proc);
  830. SCM_ASSERT (call, proc, 1, FUNC_NAME);
  831. if (!SCM_HASHTABLE_P (table))
  832. SCM_VALIDATE_VECTOR (2, table);
  833. scm_internal_hash_for_each_handle (call,
  834. (void *) SCM_UNPACK (proc),
  835. table);
  836. return SCM_UNSPECIFIED;
  837. }
  838. #undef FUNC_NAME
  839. static SCM
  840. map_proc (void *proc, SCM key, SCM data, SCM value)
  841. {
  842. return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
  843. }
  844. SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
  845. (SCM proc, SCM table),
  846. "An iterator over hash-table elements.\n"
  847. "Accumulates and returns as a list the results of applying PROC successively.\n"
  848. "The arguments to PROC are \"(key value)\" where key\n"
  849. "and value are successive pairs from the hash table TABLE.")
  850. #define FUNC_NAME s_scm_hash_map_to_list
  851. {
  852. SCM_VALIDATE_PROC (1, proc);
  853. if (!SCM_HASHTABLE_P (table))
  854. SCM_VALIDATE_VECTOR (2, table);
  855. return scm_internal_hash_fold (map_proc,
  856. (void *) SCM_UNPACK (proc),
  857. SCM_EOL,
  858. table);
  859. }
  860. #undef FUNC_NAME
  861. SCM
  862. scm_internal_hash_fold (SCM (*fn) (), void *closure, SCM init, SCM table)
  863. {
  864. long i, n;
  865. SCM buckets, result = init;
  866. if (SCM_HASHTABLE_P (table))
  867. buckets = SCM_HASHTABLE_VECTOR (table);
  868. else
  869. buckets = table;
  870. n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
  871. for (i = 0; i < n; ++i)
  872. {
  873. SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
  874. while (!scm_is_null (ls))
  875. {
  876. if (!scm_is_pair (ls))
  877. scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
  878. handle = SCM_CAR (ls);
  879. if (!scm_is_pair (handle))
  880. scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
  881. result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
  882. ls = SCM_CDR (ls);
  883. }
  884. }
  885. return result;
  886. }
  887. /* The following redundant code is here in order to be able to support
  888. hash-for-each-handle. An alternative would have been to replace
  889. this code and scm_internal_hash_fold above with a single
  890. scm_internal_hash_fold_handles, but we don't want to promote such
  891. an API. */
  892. void
  893. scm_internal_hash_for_each_handle (SCM (*fn) (), void *closure, SCM table)
  894. {
  895. long i, n;
  896. SCM buckets;
  897. if (SCM_HASHTABLE_P (table))
  898. buckets = SCM_HASHTABLE_VECTOR (table);
  899. else
  900. buckets = table;
  901. n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
  902. for (i = 0; i < n; ++i)
  903. {
  904. SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
  905. while (!scm_is_null (ls))
  906. {
  907. if (!scm_is_pair (ls))
  908. scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
  909. handle = SCM_CAR (ls);
  910. if (!scm_is_pair (handle))
  911. scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
  912. fn (closure, handle);
  913. ls = SCM_CDR (ls);
  914. }
  915. }
  916. }
  917. void
  918. scm_hashtab_prehistory ()
  919. {
  920. scm_tc16_hashtable = scm_make_smob_type (s_hashtable, 0);
  921. scm_set_smob_mark (scm_tc16_hashtable, scm_markcdr);
  922. scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
  923. scm_set_smob_free (scm_tc16_hashtable, hashtable_free);
  924. scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
  925. }
  926. void
  927. scm_init_hashtab ()
  928. {
  929. #include "libguile/hashtab.x"
  930. }
  931. /*
  932. Local Variables:
  933. c-file-style: "gnu"
  934. End:
  935. */