123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- /*
- Copyright (C) 2004, 2005, 2006 John E. Davis
- This file is part of the S-Lang Library.
- The S-Lang Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
- The S-Lang Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA.
- */
- #include "slinclud.h"
- #include "slang.h"
- #include "_slang.h"
- typedef struct _pSLstring_Type
- {
- struct _pSLstring_Type *next;
- unsigned int ref_count;
- unsigned long hash;
- unsigned int len;
- char bytes [1];
- }
- SLstring_Type;
- #define MAP_HASH_TO_INDEX(hash) ((hash) % SLSTRING_HASH_TABLE_SIZE)
- static SLstring_Type *String_Hash_Table [SLSTRING_HASH_TABLE_SIZE];
- static char Single_Char_Strings [256 * 2];
- #if SLANG_OPTIMIZE_FOR_SPEED
- #define MAX_FREE_STORE_LEN 32
- static SLstring_Type *SLS_Free_Store [MAX_FREE_STORE_LEN];
- # define NUM_CACHED_STRINGS 601
- typedef struct
- {
- SLstring_Type *sls;
- char *str;
- }
- Cached_String_Type;
- static char *Deleted_String = "*deleted*";
- static Cached_String_Type Cached_Strings [NUM_CACHED_STRINGS];
- #define GET_CACHED_STRING(s) \
- (Cached_Strings + (unsigned int)(((unsigned long) (s)) % NUM_CACHED_STRINGS))
- _INLINE_
- static void cache_string (SLstring_Type *sls)
- {
- Cached_String_Type *cs;
- cs = GET_CACHED_STRING(sls->bytes);
- cs->str = sls->bytes;
- cs->sls = sls;
- }
- _INLINE_
- static void uncache_string (char *s)
- {
- Cached_String_Type *cs;
-
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- cs->sls = NULL;
- cs->str = Deleted_String;
- }
- }
- #endif
- #if USE_NEW_HASH_CODE
- /* This hash algorithm comes from:
- *
- * Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
- * You may use this code any way you wish, private, educational, or commercial. It's free.
- * See http://burtleburtle.net/bob/hash/evahash.html
- */
- typedef unsigned long uint32;
- #define mix(a,b,c) \
- { \
- a -= b; a -= c; a ^= (c>>13); \
- b -= c; b -= a; b ^= (a<<8); \
- c -= a; c -= b; c ^= (b>>13); \
- a -= b; a -= c; a ^= (c>>12); \
- b -= c; b -= a; b ^= (a<<16); \
- c -= a; c -= b; c ^= (b>>5); \
- a -= b; a -= c; a ^= (c>>3); \
- b -= c; b -= a; b ^= (a<<10); \
- c -= a; c -= b; c ^= (b>>15); \
- }
- _INLINE_
- unsigned long _pSLstring_hash (unsigned char *s, unsigned char *smax)
- {
- register uint32 a,b,c;
- unsigned int length = (unsigned int)(smax - s);
- unsigned int len = length;
-
- a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
- c = 0;
- /*---------------------------------------- handle most of the key */
- while (len >= 12)
- {
- a += (s[0] +((uint32)s[1]<<8) +((uint32)s[2]<<16) +((uint32)s[3]<<24));
- b += (s[4] +((uint32)s[5]<<8) +((uint32)s[6]<<16) +((uint32)s[7]<<24));
- c += (s[8] +((uint32)s[9]<<8) +((uint32)s[10]<<16)+((uint32)s[11]<<24));
- mix(a,b,c);
- s += 12; len -= 12;
- }
- /*------------------------------------- handle the last 11 bytes */
- c += length;
- switch(len) /* all the case statements fall through */
- {
- case 11: c+=((uint32)s[10]<<24);
- case 10: c+=((uint32)s[9]<<16);
- case 9 : c+=((uint32)s[8]<<8);
- /* the first byte of c is reserved for the length */
- case 8 : b+=((uint32)s[7]<<24);
- case 7 : b+=((uint32)s[6]<<16);
- case 6 : b+=((uint32)s[5]<<8);
- case 5 : b+=s[4];
- case 4 : a+=((uint32)s[3]<<24);
- case 3 : a+=((uint32)s[2]<<16);
- case 2 : a+=((uint32)s[1]<<8);
- case 1 : a+=s[0];
- /* case 0: nothing left to add */
- }
- mix(a,b,c);
- /*-------------------------------------------- report the result */
- return (unsigned long) c;
- }
- #else
- _INLINE_
- unsigned long _pSLstring_hash (unsigned char *s, unsigned char *smax)
- {
- register unsigned long h = 0;
- register unsigned long sum = 0;
- unsigned char *smax4;
- smax4 = smax - 4;
- while (s < smax4)
- {
- sum += s[0];
- h = sum + (h << 1);
- sum += s[1];
- h = sum + (h << 1);
- sum += s[2];
- h = sum + (h << 1);
- sum += s[3];
- h = sum + (h << 1);
-
- s += 4;
- }
- while (s < smax)
- {
- sum += *s++;
- h ^= sum + (h << 3); /* slightly different */
- }
- return h;
- }
- #endif
- unsigned long _pSLcompute_string_hash (char *s)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- return cs->sls->hash;
- #endif
- return _pSLstring_hash ((unsigned char *) s, (unsigned char *) s + strlen (s));
- }
- _INLINE_
- /* This routine works with any (long) string */
- static SLstring_Type *find_string (char *s, unsigned int len, unsigned long hash)
- {
- SLstring_Type *sls;
- sls = String_Hash_Table [(unsigned int) MAP_HASH_TO_INDEX(hash)];
- if (sls == NULL)
- return NULL;
- do
- {
- /* Note that we need to actually make sure that bytes[len] == 0.
- * In this case, it is not enough to just compare pointers. In fact,
- * this is called from create_nstring, etc... It is unlikely that the
- * pointer is a slstring
- */
- if ((sls->hash == hash)
- && (sls->len == len)
- && (0 == strncmp (s, sls->bytes, len)))
- break;
- sls = sls->next;
- }
- while (sls != NULL);
- return sls;
- }
- _INLINE_
- static SLstring_Type *find_slstring (char *s, unsigned long hash)
- {
- SLstring_Type *sls;
- sls = String_Hash_Table [MAP_HASH_TO_INDEX(hash)];
- while (sls != NULL)
- {
- if (s == sls->bytes)
- return sls;
- sls = sls->next;
- }
- return sls;
- }
- _INLINE_
- static SLstring_Type *allocate_sls (unsigned int len)
- {
- SLstring_Type *sls;
- #if SLANG_OPTIMIZE_FOR_SPEED
-
- if ((len < MAX_FREE_STORE_LEN)
- && (NULL != (sls = SLS_Free_Store [len])))
- {
- SLS_Free_Store[len] = NULL;
- return sls;
- }
- #endif
- /* FIXME: use structure padding */
- sls = (SLstring_Type *) SLmalloc (len + sizeof (SLstring_Type));
- if (sls != NULL)
- sls->len = len;
- return sls;
- }
- _INLINE_
- static void free_sls (SLstring_Type *sls)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- unsigned int len = sls->len;
- if ((len < MAX_FREE_STORE_LEN)
- && (SLS_Free_Store[len] == NULL))
- {
- SLS_Free_Store [len] = sls;
- return;
- }
- #endif
- SLfree ((char *)sls);
- }
- _INLINE_
- static char *create_long_string (char *s, unsigned int len, unsigned long hash)
- {
- SLstring_Type *sls;
- sls = find_string (s, len, hash);
- if (sls != NULL)
- {
- sls->ref_count++;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cache_string (sls);
- #endif
- return sls->bytes;
- }
- sls = allocate_sls (len);
- if (sls == NULL)
- return NULL;
- strncpy (sls->bytes, s, len);
- sls->bytes[len] = 0;
- sls->ref_count = 1;
- sls->hash = hash;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cache_string (sls);
- #endif
- hash = MAP_HASH_TO_INDEX(hash);
- sls->next = String_Hash_Table [(unsigned int)hash];
- String_Hash_Table [(unsigned int)hash] = sls;
- return sls->bytes;
- }
- _INLINE_
- static char *create_short_string (char *s, unsigned int len)
- {
- char ch;
- /* Note: if len is 0, then it does not matter what *s is. This is
- * important for SLang_create_nslstring.
- */
- if (len) ch = *s; else ch = 0;
- len = 2 * (unsigned int) ((unsigned char) ch);
- Single_Char_Strings [len] = ch;
- Single_Char_Strings [len + 1] = 0;
- return Single_Char_Strings + len;
- }
- /* s cannot be NULL */
- _INLINE_
- static SLstr_Type *create_nstring (char *s, unsigned int len, unsigned long *hash_ptr)
- {
- unsigned long hash;
- if (len < 2)
- return create_short_string (s, len);
- hash = _pSLstring_hash ((unsigned char *) s, (unsigned char *) (s + len));
- *hash_ptr = hash;
- return create_long_string (s, len, hash);
- }
- SLstr_Type *SLang_create_nslstring (char *s, unsigned int len)
- {
- unsigned long hash;
- if (s == NULL)
- return NULL;
- return create_nstring (s, len, &hash);
- }
- char *_pSLstring_make_hashed_string (char *s, unsigned int len, unsigned long *hashptr)
- {
- unsigned long hash;
- if (s == NULL) return NULL;
- hash = _pSLstring_hash ((unsigned char *) s, (unsigned char *) s + len);
- *hashptr = hash;
- if (len < 2)
- return create_short_string (s, len);
- return create_long_string (s, len, hash);
- }
- char *_pSLstring_dup_hashed_string (char *s, unsigned long hash)
- {
- unsigned int len;
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
-
- if (s == NULL) return NULL;
- if (s[0] == 0)
- return create_short_string (s, 0);
- if (s[1] == 0)
- return create_short_string (s, 1);
-
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- cs->sls->ref_count += 1;
- return s;
- }
- #else
- if (s == NULL) return NULL;
- #endif
- len = strlen (s);
- #if !SLANG_OPTIMIZE_FOR_SPEED
- if (len < 2) return create_short_string (s, len);
- #endif
- return create_long_string (s, len, hash);
- }
- /* This function requires an slstring!!! */
- char *_pSLstring_dup_slstring (char *s)
- {
- SLstring_Type *sls;
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- #endif
-
- if (s == NULL)
- return s;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- cs->sls->ref_count += 1;
- return s;
- }
- #endif
- if ((s[0] == 0) || (s[1] == 0))
- return s;
-
- sls = (SLstring_Type *) (s - offsetof(SLstring_Type,bytes[0]));
- sls->ref_count++;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cache_string (sls);
- #endif
- return s;
- }
- static void free_sls_string (SLstring_Type *sls)
- {
- SLstring_Type *sls1, *prev;
- unsigned long hash = sls->hash;
- hash = MAP_HASH_TO_INDEX(hash);
- sls1 = String_Hash_Table [(unsigned int) hash];
- prev = NULL;
- /* This should not fail. */
- while (sls1 != sls)
- {
- prev = sls1;
- sls1 = sls1->next;
- }
- if (prev != NULL)
- prev->next = sls->next;
- else
- String_Hash_Table [(unsigned int) hash] = sls->next;
- free_sls (sls);
- }
- _INLINE_
- static void free_long_string (char *s, unsigned long hash)
- {
- SLstring_Type *sls;
- if (NULL == (sls = find_slstring (s, hash)))
- {
- SLang_verror (SL_APPLICATION_ERROR, "invalid attempt to free string:%s", s);
- return;
- }
- sls->ref_count--;
- if (sls->ref_count != 0)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- /* cache_string (sls, len, hash); */
- #endif
- return;
- }
- #if SLANG_OPTIMIZE_FOR_SPEED
- uncache_string (s);
- #endif
- free_sls_string (sls);
- }
- /* This routine may be passed NULL-- it is not an error. */
- void SLang_free_slstring (char *s)
- {
- unsigned long hash;
- unsigned int len;
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- #endif
- if (s == NULL) return;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- SLstring_Type *sls = cs->sls;
- if (sls->ref_count <= 1)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs->sls = NULL;
- cs->str = Deleted_String;
- #endif
- free_sls_string (sls);
- }
- else
- sls->ref_count -= 1;
- return;
- }
- #endif
- if ((len = strlen (s)) < 2)
- return;
- hash = _pSLstring_hash ((unsigned char *)s, (unsigned char *) s + len);
- free_long_string (s, hash);
- }
- char *SLang_create_slstring (char *s)
- {
- unsigned long hash;
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- #endif
- if (s == NULL) return NULL;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- cs->sls->ref_count += 1;
- return s;
- }
- #endif
- return create_nstring (s, strlen (s), &hash);
- }
- void _pSLfree_hashed_string (char *s, unsigned int len, unsigned long hash)
- {
- if ((s == NULL) || (len < 2)) return;
- free_long_string (s, hash);
- }
- char *_pSLallocate_slstring (unsigned int len)
- {
- SLstring_Type *sls = allocate_sls (len);
- if (sls == NULL)
- return NULL;
- sls->hash = 0;
- return sls->bytes;
- }
- void _pSLunallocate_slstring (char *s, unsigned int len)
- {
- SLstring_Type *sls;
-
- (void) len;
- if (s == NULL)
- return;
-
- sls = (SLstring_Type *) (s - offsetof(SLstring_Type,bytes[0]));
- free_sls (sls);
- }
- char *_pSLcreate_via_alloced_slstring (char *s, unsigned int len)
- {
- unsigned long hash;
- SLstring_Type *sls;
- if (s == NULL)
- return NULL;
-
- if (len < 2)
- {
- char *s1 = create_short_string (s, len);
- _pSLunallocate_slstring (s, len);
- return s1;
- }
- /* s is not going to be in the cache because when it was malloced, its
- * value was unknown. This simplifies the coding.
- */
- hash = _pSLstring_hash ((unsigned char *)s, (unsigned char *)s + len);
- sls = find_string (s, len, hash);
- if (sls != NULL)
- {
- sls->ref_count++;
- _pSLunallocate_slstring (s, len);
- s = sls->bytes;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cache_string (sls);
- #endif
- return s;
- }
-
- sls = (SLstring_Type *) (s - offsetof(SLstring_Type,bytes[0]));
- sls->ref_count = 1;
- sls->hash = hash;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cache_string (sls);
- #endif
- hash = MAP_HASH_TO_INDEX(hash);
- sls->next = String_Hash_Table [(unsigned int)hash];
- String_Hash_Table [(unsigned int)hash] = sls;
- return s;
- }
- /* Note, a and b may be ordinary strings. The result is an slstring */
- char *SLang_concat_slstrings (char *a, char *b)
- {
- unsigned int lena, len;
- char *c;
- lena = strlen (a);
- len = lena + strlen (b);
- c = _pSLallocate_slstring (len);
- if (c == NULL)
- return NULL;
- strcpy (c, a);
- strcpy (c + lena, b);
- return _pSLcreate_via_alloced_slstring (c, len);
- }
- /* This routine is assumed to work even if s is not an slstring */
- unsigned int _pSLstring_bytelen (SLstr_Type *s)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- return cs->sls->len;
- #endif
- return strlen (s);
- }
- /* The caller must ensure that this is an slstring */
- void _pSLang_free_slstring (SLstr_Type *s)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- Cached_String_Type *cs;
- #endif
- SLstring_Type *sls;
- if (s == NULL) return;
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs = GET_CACHED_STRING(s);
- if (cs->str == s)
- {
- sls = cs->sls;
- if (sls->ref_count <= 1)
- {
- #if SLANG_OPTIMIZE_FOR_SPEED
- cs->sls = NULL;
- cs->str = Deleted_String;
- #endif
- free_sls_string (sls);
- }
- else
- sls->ref_count -= 1;
- return;
- }
- #endif
-
- if ((s[0] == 0) || (s[1] == 0))
- return;
- sls = (SLstring_Type *) (s - offsetof(SLstring_Type,bytes[0]));
- if (sls->ref_count > 1)
- {
- sls->ref_count--;
- return;
- }
- free_long_string (s, sls->hash);
- }
- /* An SLstring is required */
- unsigned long _pSLstring_get_hash (SLstr_Type *s)
- {
- SLstring_Type *sls;
- if (s[0] == 0)
- return _pSLstring_hash ((unsigned char*)s, (unsigned char *)s);
- if (s[1] == 0)
- return _pSLstring_hash ((unsigned char *)s, (unsigned char *)s+1);
-
- sls = (SLstring_Type *) (s - offsetof(SLstring_Type,bytes[0]));
- return sls->hash;
- }
|