4646// Hash item, used internally only
4747
4848typedef struct _item_t {
49- void
50- * value ; // Opaque item value
51- struct _item_t
52- * next ; // Next item in the hash slot
53- qbyte
54- index ; // Index of item in table
55- char
56- * key ; // Item's original key
57- zhash_free_fn
58- * free_fn ; // Value free function if any
49+ void * value ; // Opaque item value
50+ struct _item_t * next ; // Next item in the hash slot
51+ qbyte index ; // Index of item in table
52+ char * key ; // Item's original key
53+ zhash_free_fn * free_fn ; // Value free function if any
5954} item_t ;
6055
6156
6257// ---------------------------------------------------------------------
6358// Structure of our class
6459
6560struct _zhash {
66- size_t
67- size ; // Current size of hash table
68- size_t
69- limit ; // Current hash table limit
70- item_t
71- * * items ; // Array of items
72- uint
73- cached_index ; // Avoids duplicate hash calculations
61+ size_t size ; // Current size of hash table
62+ size_t limit ; // Current hash table limit
63+ item_t * * items ; // Array of items
64+ uint cached_index ; // Avoids duplicate hash calculations
65+ bool autofree ; // If true, free values in destructor
7466};
7567
7668
@@ -81,10 +73,8 @@ struct _zhash {
8173static uint
8274s_item_hash (const char * key , size_t limit )
8375{
84- uint
85- key_hash = 0 ;
86-
8776 // Modified Bernstein hashing function
77+ uint key_hash = 0 ;
8878 while (* key )
8979 key_hash = 33 * key_hash ^ * key ++ ;
9080 key_hash %= limit ;
@@ -162,6 +152,10 @@ s_item_destroy (zhash_t *self, item_t *item, bool hard)
162152 if (hard ) {
163153 if (item -> free_fn )
164154 (item -> free_fn ) (item -> value );
155+ else
156+ if (self -> autofree )
157+ free (item -> value );
158+
165159 free (item -> key );
166160 free (item );
167161 }
@@ -271,6 +265,9 @@ zhash_update (zhash_t *self, const char *key, void *value)
271265 if (item ) {
272266 if (item -> free_fn )
273267 (item -> free_fn ) (item -> value );
268+ else
269+ if (self -> autofree )
270+ free (item -> value );
274271 item -> value = value ;
275272 }
276273 else
@@ -386,20 +383,21 @@ zhash_dup (zhash_t *self)
386383 return NULL ;
387384
388385 zhash_t * copy = zhash_new ();
386+ zhash_autofree (copy );
389387 if (copy ) {
390388 uint index ;
391389 for (index = 0 ; index != self -> limit ; index ++ ) {
392390 item_t * item = self -> items [index ];
393391 while (item ) {
394392 zhash_insert (copy , item -> key , strdup (item -> value ));
395- zhash_freefn (copy , item -> key , free );
396393 item = item -> next ;
397394 }
398395 }
399396 }
400397 return copy ;
401398}
402399
400+
403401// --------------------------------------------------------------------------
404402// Return keys for items in table
405403
@@ -448,6 +446,76 @@ zhash_foreach (zhash_t *self, zhash_foreach_fn *callback, void *argument)
448446}
449447
450448
449+ // --------------------------------------------------------------------------
450+ // Save hash table to a text file in name=value format
451+ // Hash values must be printable strings; keys may not contain '=' character
452+ // Returns 0 if OK, else -1 if a file error occurred
453+
454+ int
455+ zhash_save (zhash_t * self , char * filename )
456+ {
457+ assert (self );
458+
459+ FILE * handle = fopen (filename , "w" );
460+ if (!handle )
461+ return -1 ; // Failed to create file
462+
463+ uint index ;
464+ for (index = 0 ; index != self -> limit ; index ++ ) {
465+ item_t * item = self -> items [index ];
466+ while (item ) {
467+ fprintf (handle , "%s=%s\n" , item -> key , (char * ) item -> value );
468+ item = item -> next ;
469+ }
470+ }
471+ fclose (handle );
472+ return 0 ;
473+ }
474+
475+
476+ // --------------------------------------------------------------------------
477+ // Load hash table from a text file in name=value format; hash table must
478+ // already exist. Hash values must printable strings; keys may not contain
479+ // '=' character. Returns 0 if OK, else -1 if a file was not readable.
480+
481+ int
482+ zhash_load (zhash_t * self , char * filename )
483+ {
484+ assert (self );
485+ zhash_autofree (self );
486+
487+ FILE * handle = fopen (filename , "r" );
488+ if (!handle )
489+ return -1 ; // Failed to create file
490+
491+ char buffer [1024 ];
492+ while (fgets (buffer , 1024 , handle )) {
493+ // Buffer may end in newline, which we don't want
494+ if (buffer [strlen (buffer ) - 1 ] == '\n' )
495+ buffer [strlen (buffer ) - 1 ] = 0 ;
496+ // Split at equals, if any
497+ char * equals = strchr (buffer , '=' );
498+ if (!equals )
499+ break ; // Some error, stop parsing it
500+ * equals ++ = 0 ;
501+ zhash_update (self , buffer , strdup (equals ));
502+ }
503+ fclose (handle );
504+ return 0 ;
505+ }
506+
507+
508+ // --------------------------------------------------------------------------
509+ // Set hash for automatic value destruction
510+
511+ void
512+ zhash_autofree (zhash_t * self )
513+ {
514+ assert (self );
515+ self -> autofree = true;
516+ }
517+
518+
451519// --------------------------------------------------------------------------
452520// Runs selftest of class
453521// TODO: add unit test for free_fn, foreach
@@ -509,8 +577,25 @@ zhash_test (int verbose)
509577 // Test dup method
510578 zhash_t * copy = zhash_dup (hash );
511579 assert (zhash_size (copy ) == 4 );
580+ item = zhash_lookup (copy , "LIVEBEEF" );
581+ assert (item );
582+ assert (streq (item , "dead beef" ));
512583 zhash_destroy (& copy );
513584
585+ // Test save and load
586+ zhash_save (hash , ".cache" );
587+ copy = zhash_new ();
588+ zhash_load (copy , ".cache" );
589+ item = zhash_lookup (copy , "LIVEBEEF" );
590+ assert (item );
591+ assert (streq (item , "dead beef" ));
592+ zhash_destroy (& copy );
593+ #if (defined (WIN32 ))
594+ DeleteFile (".cache" );
595+ #else
596+ unlink (".cache" );
597+ #endif
598+
514599 // Delete a item
515600 zhash_delete (hash , "LIVEBEEF" );
516601 item = zhash_lookup (hash , "LIVEBEEF" );
0 commit comments