Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| SHA1 Hash: | dfd4a3dfd063c17ad760c77c2320bad69ccfac59 |
|---|---|
| Date: | 2008-11-21 16:25:00 |
| User: | stephan |
| Comment: | added whst |
Tags And Properties
- branch=trunk inherited from [2bd6172b61]
- sym-trunk inherited from [2bd6172b61]
Changes
Changes to Makefile
| Old (6c7cd75103ca0205) | New (e136c51f53d75cda) | |||
|---|---|---|---|---|
| 1 | #!/usr/bin/make -f | 1 | #!/usr/bin/make -f | |
| 2 | # Requires GNU Make 3.80+! | 2 | # Requires GNU Make 3.80+! | |
| 3 | default: all | 3 | default: all | |
| 4 | 4 | |||
| 5 | ifeq (1,$(TCC)) | 5 | ifeq (1,$(TCC)) | |
| 34 hidden lines | ||||
| 40 | libwhhash.LIB.OBJECTS = $(HASH_OBJ) | 40 | libwhhash.LIB.OBJECTS = $(HASH_OBJ) | |
| 41 | $(call ShakeNMake.CALL.RULES.LIBS,libwhhash) | 41 | $(call ShakeNMake.CALL.RULES.LIBS,libwhhash) | |
| 42 | 42 | |||
| 43 | ######################################################################## | 43 | ######################################################################## | |
| 44 | # GC lib: | 44 | # GC lib: | |
| 45 | WHGC_OBJ := whgc.o whrc.o | | | 45 | WHGC_OBJ := whgc.o whrc.o whst.o |
| 46 | libwhgc.LIB.OBJECTS = $(WHGC_OBJ) $(HASH_OBJ) | 46 | libwhgc.LIB.OBJECTS = $(WHGC_OBJ) $(HASH_OBJ) | |
| 47 | $(call ShakeNMake.CALL.RULES.LIBS,libwhgc) | 47 | $(call ShakeNMake.CALL.RULES.LIBS,libwhgc) | |
| 48 | $(libwhgc.LIB): $(libwhhash.LIB) | 48 | $(libwhgc.LIB): $(libwhhash.LIB) | |
| 49 | libs: $(libwhgc.LIB) | 49 | libs: $(libwhgc.LIB) | |
| 50 | 50 | |||
| 6 hidden lines | ||||
| 57 | CLEAN_FILES += *~ *.valgrind *.a | 57 | CLEAN_FILES += *~ *.valgrind *.a | |
| 58 | 58 | |||
| 59 | PACKAGE.DIST_FILES += $(wildcard *.c *.h) | 59 | PACKAGE.DIST_FILES += $(wildcard *.c *.h) | |
| 60 | 60 | |||
| 61 | all: libs bins | 61 | all: libs bins | |
Changes to test.c
| Old (c4bd24e3ba1c76f7) | New (65c58898d96b6066) | |||
|---|---|---|---|---|
| 1 | #include <stdio.h> | 1 | #include <stdio.h> | |
| 2 | #include <stdlib.h> | 2 | #include <stdlib.h> | |
| 3 | #include <string.h> | 3 | #include <string.h> | |
| 4 | #include <ctype.h> | 4 | #include <ctype.h> | |
| 5 | #include "whrc.h" | 5 | #include "whrc.h" | |
| > | 6 | #include "whst.h" | ||
| 6 | 7 | |||
| 7 | #if 1 | 8 | #if 1 | |
| 8 | #include <stdio.h> // only for debuggering | 9 | #include <stdio.h> // only for debuggering | |
| 9 | #define MARKER if(1) printf("MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__); if(1) printf | 10 | #define MARKER if(1) printf("MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__); if(1) printf | |
| 10 | #else | 11 | #else | |
| 17 hidden lines | ||||
| 28 | MARKER("refcount=%u\n",rc); | 29 | MARKER("refcount=%u\n",rc); | |
| 29 | rc = whrc_rmref(cx,str); // rc == 1 | 30 | rc = whrc_rmref(cx,str); // rc == 1 | |
| 30 | MARKER("refcount=%u\n",rc); | 31 | MARKER("refcount=%u\n",rc); | |
| 31 | rc = whrc_rmref(cx,str); // rc == 0, free(str) is called | 32 | rc = whrc_rmref(cx,str); // rc == 0, free(str) is called | |
| 32 | MARKER("refcount=%u\n",rc); | 33 | MARKER("refcount=%u\n",rc); | |
| 33 | rc = whrc_rmref(cx,str); // rc == 0, free(str) is called | | | 34 | rc = whrc_rmref(cx,str); // rc == whrc_ref_err_val |
| 34 | MARKER("refcount=%u\n",rc); | 35 | MARKER("refcount=%u\n",rc); | |
| 35 | //whrc_destroy_context(cx,true); | | | 36 | whrc_destroy_context(cx,true); |
| 36 | MARKER("ending test\n"); | 37 | MARKER("ending test\n"); | |
| > | 38 | return 0; | ||
| > | 39 | } | ||
| > | 40 | |||
| > | 41 | int test_whst() | ||
| > | 42 | { | ||
| > | 43 | MARKER("starting\n"); | ||
| > | 44 | |||
| > | 45 | whst_context * cx = 0; | ||
| > | 46 | cx = whst_create_context(); | ||
| > | 47 | #if 1 | ||
| > | 48 | char const * str = "this is the string"; | ||
| > | 49 | char const * str2 = "this is the other string"; | ||
| > | 50 | size_t i = 0; | ||
| > | 51 | const size_t max = 20; | ||
| > | 52 | char const * strR; | ||
| > | 53 | char const * strR2; | ||
| > | 54 | for( ; i < max; ++i ) | ||
| > | 55 | { | ||
| > | 56 | strR = whst_addref( cx, str ); | ||
| > | 57 | strR2 = whst_addref( cx, str2 ); | ||
| > | 58 | } | ||
| > | 59 | MARKER("refcounts=%u %u\n",whst_refcount(cx,strR), whst_refcount(cx,strR2)); | ||
| > | 60 | for( i = 0; i < max; ++i ) | ||
| > | 61 | { | ||
| > | 62 | whst_rmref( cx, strR ); | ||
| > | 63 | if( 0 == (i%3) ) whst_rmref( cx, strR2 ); | ||
| > | 64 | } | ||
| > | 65 | MARKER("refcounts=%u %u\n",whst_refcount(cx,strR), whst_refcount(cx,strR2)); | ||
| > | 66 | MARKER("addresses=[%p==%p] [%p==%p]\n",str,strR,str2,strR2); | ||
| > | 67 | #endif | ||
| > | 68 | whst_destroy_context(cx); | ||
| > | 69 | MARKER("ending\n"); | ||
| 37 | return 0; | 70 | return 0; | |
| 38 | } | 71 | } | |
| 39 | 72 | |||
| 40 | int main( int argc, char ** argv ) | 73 | int main( int argc, char ** argv ) | |
| 41 | { | 74 | { | |
| 42 | int rc = 0; | 75 | int rc = 0; | |
| 43 | rc = test_one(); | | | 76 | //if(!rc) rc = test_one(); |
| | | 77 | if(!rc) rc = test_whst(); | ||
| 44 | printf("Done rc=%d=[%s].\n",rc, | 78 | printf("Done rc=%d=[%s].\n",rc, | |
| 45 | (0==rc) | 79 | (0==rc) | |
| 46 | ? "You win :)" | 80 | ? "You win :)" | |
| 47 | : "You lose :("); | 81 | : "You lose :("); | |
| 48 | return rc; | 82 | return rc; | |
| 49 | } | 83 | } | |
Added whst.c
| Old () | New (46b70e6683b88cff) | |||
|---|---|---|---|---|
| > | 1 | #include <stdlib.h> /* malloc/free */ | ||
| > | 2 | #include <assert.h> | ||
| > | 3 | #include <string.h> | ||
| > | 4 | |||
| > | 5 | #include "whst.h" | ||
| > | 6 | #include "whhash.h" | ||
| > | 7 | #ifdef __cplusplus | ||
| > | 8 | extern "C" { | ||
| > | 9 | #endif | ||
| > | 10 | |||
| > | 11 | #if 1 | ||
| > | 12 | #include <stdio.h> // only for debuggering | ||
| > | 13 | #define MARKER if(1) printf("MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__); if(1) printf | ||
| > | 14 | #else | ||
| > | 15 | static void bogo_printf(char const * fmt, ...) {} | ||
| > | 16 | #define MARKER if(0) bogo_printf | ||
| > | 17 | #endif | ||
| > | 18 | |||
| > | 19 | const size_t whst_ref_err_val = ((size_t)-1); | ||
| > | 20 | struct whst_context | ||
| > | 21 | { | ||
| > | 22 | whhash_table * ht; | ||
| > | 23 | }; | ||
| > | 24 | |||
| > | 25 | static const whst_context whst_context_init = | ||
| > | 26 | { | ||
| > | 27 | 0 /* ht */ | ||
| > | 28 | }; | ||
| > | 29 | |||
| > | 30 | struct whst_entry | ||
| > | 31 | { | ||
| > | 32 | char * string; | ||
| > | 33 | size_t refcount; | ||
| > | 34 | }; | ||
| > | 35 | typedef struct whst_entry whst_entry; | ||
| > | 36 | static const whst_entry whst_entry_init = {0,0}; | ||
| > | 37 | |||
| > | 38 | |||
| > | 39 | static void whst_entry_destroy( whst_entry * e) | ||
| > | 40 | { | ||
| > | 41 | if( e ) | ||
| > | 42 | { | ||
| > | 43 | free(e->string); | ||
| > | 44 | free(e); | ||
| > | 45 | } | ||
| > | 46 | } | ||
| > | 47 | /** | ||
| > | 48 | For use as a whhash_table entry dtor. | ||
| > | 49 | */ | ||
| > | 50 | static void whst_entry_dtor( void * x) | ||
| > | 51 | { | ||
| > | 52 | whst_entry_destroy( (whst_entry*)x ); | ||
| > | 53 | } | ||
| > | 54 | |||
| > | 55 | |||
| > | 56 | whst_context * whst_create_context() | ||
| > | 57 | { | ||
| > | 58 | whst_context * c = (whst_context *)malloc(sizeof(whst_context)); | ||
| > | 59 | if( ! c ) return 0; | ||
| > | 60 | *c = whst_context_init; | ||
| > | 61 | if( (c->ht = whhash_create( 10, whhash_hash_cstring_djb2m, whhash_cmp_cstring ) ) ) | ||
| > | 62 | { | ||
| > | 63 | whhash_set_dtors( c->ht, 0, whst_entry_dtor ); | ||
| > | 64 | } | ||
| > | 65 | else | ||
| > | 66 | { | ||
| > | 67 | free(c); | ||
| > | 68 | c = 0; | ||
| > | 69 | } | ||
| > | 70 | return c; | ||
| > | 71 | } | ||
| > | 72 | |||
| > | 73 | void whst_clear_context( whst_context * cx ) | ||
| > | 74 | { | ||
| > | 75 | whhash_clear(cx->ht); | ||
| > | 76 | /* TODO: also recreate the hashtable, since that's the only way | ||
| > | 77 | to shrink it. | ||
| > | 78 | */ | ||
| > | 79 | } | ||
| > | 80 | |||
| > | 81 | void whst_destroy_context( whst_context * cx ) | ||
| > | 82 | { | ||
| > | 83 | if( cx ) | ||
| > | 84 | { | ||
| > | 85 | whst_clear_context(cx); | ||
| > | 86 | whhash_destroy( cx->ht ); | ||
| > | 87 | *cx = whst_context_init; | ||
| > | 88 | free(cx); | ||
| > | 89 | } | ||
| > | 90 | } | ||
| > | 91 | |||
| > | 92 | static whst_entry * whst_search_entry( whst_context * cx, char const * item ) | ||
| > | 93 | { | ||
| > | 94 | return (cx && item) | ||
| > | 95 | ? (whst_entry *) whhash_search(cx->ht, item) | ||
| > | 96 | : 0; | ||
| > | 97 | } | ||
| > | 98 | |||
| > | 99 | size_t whst_refcount( whst_context * cx, char const * item ) | ||
| > | 100 | { | ||
| > | 101 | whst_entry const * e = whst_search_entry(cx, item); | ||
| > | 102 | return e ? e->refcount : 0; | ||
| > | 103 | } | ||
| > | 104 | |||
| > | 105 | |||
| > | 106 | |||
| > | 107 | static whst_entry * whst_take_entry( whst_context * cx, char const * item ) | ||
| > | 108 | { | ||
| > | 109 | return cx | ||
| > | 110 | ? (whst_entry *) whhash_take(cx->ht, item) | ||
| > | 111 | : 0; | ||
| > | 112 | } | ||
| > | 113 | |||
| > | 114 | char * whst_copy_string( char const * str, size_t len ) | ||
| > | 115 | { | ||
| > | 116 | char * rc = 0; | ||
| > | 117 | if( str && !len ) len = (str ? strlen(str) : 0); | ||
| > | 118 | if( !len ) return 0; | ||
| > | 119 | rc = (char * )malloc(len+1); | ||
| > | 120 | if( rc ) | ||
| > | 121 | { | ||
| > | 122 | size_t i = 0; | ||
| > | 123 | for( ; (i < len) && str[i]; ++i ) rc[i] = str[i]; | ||
| > | 124 | for( ; i < len; ++i ) rc[i] = 0; | ||
| > | 125 | } | ||
| > | 126 | return rc; | ||
| > | 127 | } | ||
| > | 128 | |||
| > | 129 | char const * whst_addref( whst_context * cx, char const * item ) | ||
| > | 130 | { | ||
| > | 131 | if( ! cx || !item ) return 0; | ||
| > | 132 | whst_entry * e = whst_search_entry(cx,item); | ||
| > | 133 | if( e ) | ||
| > | 134 | { | ||
| > | 135 | ++e->refcount; | ||
| > | 136 | return e->string; | ||
| > | 137 | } | ||
| > | 138 | e = (whst_entry*)malloc(sizeof(whst_entry)); | ||
| > | 139 | if( ! e ) return 0; | ||
| > | 140 | *e = whst_entry_init; | ||
| > | 141 | e->refcount = 1; | ||
| > | 142 | e->string = whst_copy_string( item, 0 ); | ||
| > | 143 | if( ! whhash_insert( cx->ht, e->string, e ) ) | ||
| > | 144 | { | ||
| > | 145 | whst_entry_destroy( e ); | ||
| > | 146 | e = 0; | ||
| > | 147 | } | ||
| > | 148 | return e ? e->string : 0; | ||
| > | 149 | } | ||
| > | 150 | |||
| > | 151 | size_t whst_rmref( whst_context * cx, char const * item ) | ||
| > | 152 | { | ||
| > | 153 | whst_entry * e = whst_search_entry(cx,item); | ||
| > | 154 | //MARKER("e=@%p, rc=%u\n",e,e?e->refcount:whst_ref_err_val); | ||
| > | 155 | if( ! e ) return 0; | ||
| > | 156 | size_t rc = --e->refcount; | ||
| > | 157 | if( 0 == rc ) | ||
| > | 158 | { | ||
| > | 159 | whst_entry * check = whst_take_entry(cx, item); | ||
| > | 160 | assert( (check == e) && "Internal state error - maybe another thread has trashed our hashtable?"); | ||
| > | 161 | whst_entry_destroy( check ); | ||
| > | 162 | } | ||
| > | 163 | return rc; | ||
| > | 164 | } | ||
| > | 165 | |||
| > | 166 | #ifdef __cplusplus | ||
| > | 167 | } /* extern "C" */ | ||
| > | 168 | #endif | ||
Added whst.h
| Old () | New (115900f7df5f45ce) | |||
|---|---|---|---|---|
| > | 1 | #ifndef WANDERINGHORSE_NET_WHST_H_INCLUDED | ||
| > | 2 | #define WANDERINGHORSE_NET_WHST_H_INCLUDED 1 | ||
| > | 3 | /** @page whst_page_main whst: reference counting string pool for C | ||
| > | 4 | |||
| > | 5 | whst (the WanderingHorse.net String Table) is a small C library for | ||
| > | 6 | adding reference counts to strings. It is intended for applications where | ||
| > | 7 | many strings may need to be copied, but for which many duplicates | ||
| > | 8 | are expected. By adding the string to the pool, we can often void | ||
| > | 9 | another string allocation. This is only useful for avoiding mallocs | ||
| > | 10 | when many dupes are expected, as the reference counting takes | ||
| > | 11 | requires approximately 3 internal allocs for hashtable entries. | ||
| > | 12 | |||
| > | 13 | It internally uses a hashtable which uses a hash code based on the | ||
| > | 14 | string value of an item. In theory the hashtable can average 0(1) | ||
| > | 15 | speeds, so there is very little performance hit. That said, the hash | ||
| > | 16 | routine is O(N), where N is the length of the string. | ||
| > | 17 | |||
| > | 18 | Example: | ||
| > | 19 | @code | ||
| > | 20 | whst_context * cx = whst_create_context(); | ||
| > | 21 | char const * str = "hi, world"; | ||
| > | 22 | whst_addref( cx, str ); | ||
| > | 23 | size_t rc = whst_refcount(cx,str); // rc == 1 | ||
| > | 24 | rc = whst_addref(cx,str); //rc == 2 | ||
| > | 25 | rc = whst_rmref(cx,str); // rc == 1 | ||
| > | 26 | rc = whst_rmref(cx,str); // rc == 0, string is internally cleaned up | ||
| > | 27 | whst_destroy_context(cx,true); | ||
| > | 28 | @endcode | ||
| > | 29 | |||
| > | 30 | When adding a new string it must make a copy of that string. | ||
| > | 31 | |||
| > | 32 | That demonstrates the majority of the API. There are only a handful of | ||
| > | 33 | functions to learn. | ||
| > | 34 | |||
| > | 35 | @section whst_sec_threadsafety Thread safety | ||
| > | 36 | |||
| > | 37 | By default it is never legal to use the same @ref whst_context from | ||
| > | 38 | multiple threads at the same time. That said, the client may use | ||
| > | 39 | their own locking to serialize access. All API functions which | ||
| > | 40 | take a (@ref whst_context const *) argument require only a read lock, | ||
| > | 41 | whereas those taking a (@ref whst_context *) argument require a | ||
| > | 42 | exclusive (read/write) access. whst_create_context() is reentrant | ||
| > | 43 | and does not need to be locked. | ||
| > | 44 | */ | ||
| > | 45 | |||
| > | 46 | #include <stddef.h> /* size_t */ | ||
| > | 47 | |||
| > | 48 | #ifdef __cplusplus | ||
| > | 49 | extern "C" { | ||
| > | 50 | #endif | ||
| > | 51 | |||
| > | 52 | #ifndef __cplusplus | ||
| > | 53 | # ifndef WHGC_HAVE_STDBOOL | ||
| > | 54 | # define WHGC_HAVE_STDBOOL 0 | ||
| > | 55 | # endif | ||
| > | 56 | # if defined(WHST_HAVE_STDBOOL) && !(WHST_HAVE_STDBOOL) | ||
| > | 57 | # if !defined(bool) | ||
| > | 58 | # define bool char | ||
| > | 59 | # endif | ||
| > | 60 | # if !defined(true) | ||
| > | 61 | # define true 1 | ||
| > | 62 | # endif | ||
| > | 63 | # if !defined(false) | ||
| > | 64 | # define false 0 | ||
| > | 65 | # endif | ||
| > | 66 | # else /* aha! stdbool.h! C99. */ | ||
| > | 67 | # include <stdbool.h> | ||
| > | 68 | # endif /* WHST_HAVE_STDBOOL */ | ||
| > | 69 | #endif /* __cplusplus */ | ||
| > | 70 | |||
| > | 71 | /** | ||
| > | 72 | Defined to ((size_t)-1), and returned from some functions | ||
| > | 73 | which need to report an error. | ||
| > | 74 | */ | ||
| > | 75 | extern const size_t whst_ref_err_val; | ||
| > | 76 | |||
| > | 77 | /**@typedef struct whst_context whst_context | ||
| > | 78 | |||
| > | 79 | Opaque handle type for reference count operations. | ||
| > | 80 | */ | ||
| > | 81 | struct whst_context; | ||
| > | 82 | typedef struct whst_context whst_context; | ||
| > | 83 | |||
| > | 84 | /** | ||
| > | 85 | Creates a new whst_context object and transfers ownership to the | ||
| > | 86 | caller. The caller must free it with whst_destroy_context(). | ||
| > | 87 | */ | ||
| > | 88 | whst_context * whst_create_context(); | ||
| > | 89 | |||
| > | 90 | /** | ||
| > | 91 | Clears all items in a context, unconditionally passing them to | ||
| > | 92 | their registered destructors. | ||
| > | 93 | */ | ||
| > | 94 | void whst_clear_context( whst_context * ); | ||
| > | 95 | |||
| > | 96 | /** | ||
| > | 97 | Like whst_clear_context() , but also frees cx. | ||
| > | 98 | */ | ||
| > | 99 | void whst_destroy_context( whst_context * cx ); | ||
| > | 100 | |||
| > | 101 | /** | ||
| > | 102 | Adds one to the reference count of the given string. If the string | ||
| > | 103 | has not yet been registered then a new copy is created and its | ||
| > | 104 | reference count is set to 1. The internal copy of the string is | ||
| > | 105 | returned. It is owned by cx and will be cleaned up when the | ||
| > | 106 | refcount goes to 0 or cx is destroy. | ||
| > | 107 | |||
| > | 108 | Returns 0 on an allocation error or if !cx or !item. | ||
| > | 109 | */ | ||
| > | 110 | char const * whst_addref( whst_context * cx, char const * item ); | ||
| > | 111 | |||
| > | 112 | /** | ||
| > | 113 | Subtracts one to the reference count of item and returns the new | ||
| > | 114 | reference count. It returns 0 if (!cx) or (!item), or if no entry | ||
| > | 115 | is found, but this is indistinguishable from a refcount which just | ||
| > | 116 | dropped to 0. | ||
| > | 117 | |||
| > | 118 | If the refcount is reduced to 0 then the internal string copy is | ||
| > | 119 | cleaned up. | ||
| > | 120 | */ | ||
| > | 121 | size_t whst_rmref( whst_context * cx, char const * item ); | ||
| > | 122 | |||
| > | 123 | /** | ||
| > | 124 | Returns the current refcount for the given string, or | ||
| > | 125 | 0 if no such item is found or if cx or item are 0. | ||
| > | 126 | */ | ||
| > | 127 | size_t whst_refcount( whst_context * cx, char const * item ); | ||
| > | 128 | |||
| > | 129 | |||
| > | 130 | #ifdef __cplusplus | ||
| > | 131 | } /* extern "C" */ | ||
| > | 132 | #endif | ||
| > | 133 | |||
| > | 134 | |||
| > | 135 | #endif /* WANDERINGHORSE_NET_WHST_H_INCLUDED */ | ||