2 Copyright (C) 2004, 2005, 2006 John E. Davis
4 This file is part of the S-Lang Library.
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 typedef struct _pSLstring_Type
29 struct _pSLstring_Type
*next
;
30 unsigned int ref_count
;
37 #define MAP_HASH_TO_INDEX(hash) ((hash) % SLSTRING_HASH_TABLE_SIZE)
39 static SLstring_Type
*String_Hash_Table
[SLSTRING_HASH_TABLE_SIZE
];
40 static char Single_Char_Strings
[256 * 2];
42 #if SLANG_OPTIMIZE_FOR_SPEED
43 #define MAX_FREE_STORE_LEN 32
44 static SLstring_Type
*SLS_Free_Store
[MAX_FREE_STORE_LEN
];
46 # define NUM_CACHED_STRINGS 601
53 static char *Deleted_String
= "*deleted*";
54 static Cached_String_Type Cached_Strings
[NUM_CACHED_STRINGS
];
56 #define GET_CACHED_STRING(s) \
57 (Cached_Strings + (unsigned int)(((unsigned long) (s)) % NUM_CACHED_STRINGS))
61 static void cache_string (SLstring_Type
*sls
)
63 Cached_String_Type
*cs
;
65 cs
= GET_CACHED_STRING(sls
->bytes
);
71 static void uncache_string (char *s
)
73 Cached_String_Type
*cs
;
75 cs
= GET_CACHED_STRING(s
);
79 cs
->str
= Deleted_String
;
85 /* This hash algorithm comes from:
87 * Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
88 * You may use this code any way you wish, private, educational, or commercial. It's free.
89 * See http://burtleburtle.net/bob/hash/evahash.html
92 typedef unsigned long uint32
;
96 a -= b; a -= c; a ^= (c>>13); \
97 b -= c; b -= a; b ^= (a<<8); \
98 c -= a; c -= b; c ^= (b>>13); \
99 a -= b; a -= c; a ^= (c>>12); \
100 b -= c; b -= a; b ^= (a<<16); \
101 c -= a; c -= b; c ^= (b>>5); \
102 a -= b; a -= c; a ^= (c>>3); \
103 b -= c; b -= a; b ^= (a<<10); \
104 c -= a; c -= b; c ^= (b>>15); \
108 unsigned long _pSLstring_hash (unsigned char *s
, unsigned char *smax
)
110 register uint32 a
,b
,c
;
111 unsigned int length
= (unsigned int)(smax
- s
);
112 unsigned int len
= length
;
114 a
= b
= 0x9e3779b9; /* the golden ratio; an arbitrary value */
117 /*---------------------------------------- handle most of the key */
120 a
+= (s
[0] +((uint32
)s
[1]<<8) +((uint32
)s
[2]<<16) +((uint32
)s
[3]<<24));
121 b
+= (s
[4] +((uint32
)s
[5]<<8) +((uint32
)s
[6]<<16) +((uint32
)s
[7]<<24));
122 c
+= (s
[8] +((uint32
)s
[9]<<8) +((uint32
)s
[10]<<16)+((uint32
)s
[11]<<24));
127 /*------------------------------------- handle the last 11 bytes */
129 switch(len
) /* all the case statements fall through */
131 case 11: c
+=((uint32
)s
[10]<<24);
132 case 10: c
+=((uint32
)s
[9]<<16);
133 case 9 : c
+=((uint32
)s
[8]<<8);
134 /* the first byte of c is reserved for the length */
135 case 8 : b
+=((uint32
)s
[7]<<24);
136 case 7 : b
+=((uint32
)s
[6]<<16);
137 case 6 : b
+=((uint32
)s
[5]<<8);
139 case 4 : a
+=((uint32
)s
[3]<<24);
140 case 3 : a
+=((uint32
)s
[2]<<16);
141 case 2 : a
+=((uint32
)s
[1]<<8);
143 /* case 0: nothing left to add */
146 /*-------------------------------------------- report the result */
147 return (unsigned long) c
;
151 unsigned long _pSLstring_hash (unsigned char *s
, unsigned char *smax
)
153 register unsigned long h
= 0;
154 register unsigned long sum
= 0;
155 unsigned char *smax4
;
176 h
^= sum
+ (h
<< 3); /* slightly different */
182 unsigned long _pSLcompute_string_hash (char *s
)
184 #if SLANG_OPTIMIZE_FOR_SPEED
185 Cached_String_Type
*cs
;
187 cs
= GET_CACHED_STRING(s
);
189 return cs
->sls
->hash
;
191 return _pSLstring_hash ((unsigned char *) s
, (unsigned char *) s
+ strlen (s
));
195 /* This routine works with any (long) string */
196 static SLstring_Type
*find_string (char *s
, unsigned int len
, unsigned long hash
)
200 sls
= String_Hash_Table
[(unsigned int) MAP_HASH_TO_INDEX(hash
)];
207 /* Note that we need to actually make sure that bytes[len] == 0.
208 * In this case, it is not enough to just compare pointers. In fact,
209 * this is called from create_nstring, etc... It is unlikely that the
210 * pointer is a slstring
212 if ((sls
->hash
== hash
)
214 && (0 == strncmp (s
, sls
->bytes
, len
)))
225 static SLstring_Type
*find_slstring (char *s
, unsigned long hash
)
229 sls
= String_Hash_Table
[MAP_HASH_TO_INDEX(hash
)];
241 static SLstring_Type
*allocate_sls (unsigned int len
)
244 #if SLANG_OPTIMIZE_FOR_SPEED
246 if ((len
< MAX_FREE_STORE_LEN
)
247 && (NULL
!= (sls
= SLS_Free_Store
[len
])))
249 SLS_Free_Store
[len
] = NULL
;
253 /* FIXME: use structure padding */
254 sls
= (SLstring_Type
*) SLmalloc (len
+ sizeof (SLstring_Type
));
261 static void free_sls (SLstring_Type
*sls
)
263 #if SLANG_OPTIMIZE_FOR_SPEED
264 unsigned int len
= sls
->len
;
265 if ((len
< MAX_FREE_STORE_LEN
)
266 && (SLS_Free_Store
[len
] == NULL
))
268 SLS_Free_Store
[len
] = sls
;
272 SLfree ((char *)sls
);
276 static char *create_long_string (char *s
, unsigned int len
, unsigned long hash
)
280 sls
= find_string (s
, len
, hash
);
285 #if SLANG_OPTIMIZE_FOR_SPEED
291 sls
= allocate_sls (len
);
295 strncpy (sls
->bytes
, s
, len
);
299 #if SLANG_OPTIMIZE_FOR_SPEED
303 hash
= MAP_HASH_TO_INDEX(hash
);
304 sls
->next
= String_Hash_Table
[(unsigned int)hash
];
305 String_Hash_Table
[(unsigned int)hash
] = sls
;
311 static char *create_short_string (char *s
, unsigned int len
)
315 /* Note: if len is 0, then it does not matter what *s is. This is
316 * important for SLang_create_nslstring.
318 if (len
) ch
= *s
; else ch
= 0;
320 len
= 2 * (unsigned int) ((unsigned char) ch
);
321 Single_Char_Strings
[len
] = ch
;
322 Single_Char_Strings
[len
+ 1] = 0;
323 return Single_Char_Strings
+ len
;
326 /* s cannot be NULL */
328 static SLstr_Type
*create_nstring (char *s
, unsigned int len
, unsigned long *hash_ptr
)
333 return create_short_string (s
, len
);
335 hash
= _pSLstring_hash ((unsigned char *) s
, (unsigned char *) (s
+ len
));
338 return create_long_string (s
, len
, hash
);
341 SLstr_Type
*SLang_create_nslstring (char *s
, unsigned int len
)
346 return create_nstring (s
, len
, &hash
);
349 char *_pSLstring_make_hashed_string (char *s
, unsigned int len
, unsigned long *hashptr
)
353 if (s
== NULL
) return NULL
;
355 hash
= _pSLstring_hash ((unsigned char *) s
, (unsigned char *) s
+ len
);
359 return create_short_string (s
, len
);
361 return create_long_string (s
, len
, hash
);
364 char *_pSLstring_dup_hashed_string (char *s
, unsigned long hash
)
367 #if SLANG_OPTIMIZE_FOR_SPEED
368 Cached_String_Type
*cs
;
370 if (s
== NULL
) return NULL
;
372 return create_short_string (s
, 0);
374 return create_short_string (s
, 1);
376 cs
= GET_CACHED_STRING(s
);
379 cs
->sls
->ref_count
+= 1;
383 if (s
== NULL
) return NULL
;
387 #if !SLANG_OPTIMIZE_FOR_SPEED
388 if (len
< 2) return create_short_string (s
, len
);
391 return create_long_string (s
, len
, hash
);
394 /* This function requires an slstring!!! */
395 char *_pSLstring_dup_slstring (char *s
)
398 #if SLANG_OPTIMIZE_FOR_SPEED
399 Cached_String_Type
*cs
;
404 #if SLANG_OPTIMIZE_FOR_SPEED
405 cs
= GET_CACHED_STRING(s
);
408 cs
->sls
->ref_count
+= 1;
412 if ((s
[0] == 0) || (s
[1] == 0))
415 sls
= (SLstring_Type
*) (s
- offsetof(SLstring_Type
,bytes
[0]));
417 #if SLANG_OPTIMIZE_FOR_SPEED
423 static void free_sls_string (SLstring_Type
*sls
)
425 SLstring_Type
*sls1
, *prev
;
426 unsigned long hash
= sls
->hash
;
428 hash
= MAP_HASH_TO_INDEX(hash
);
430 sls1
= String_Hash_Table
[(unsigned int) hash
];
434 /* This should not fail. */
442 prev
->next
= sls
->next
;
444 String_Hash_Table
[(unsigned int) hash
] = sls
->next
;
450 static void free_long_string (char *s
, unsigned long hash
)
454 if (NULL
== (sls
= find_slstring (s
, hash
)))
456 SLang_verror (SL_APPLICATION_ERROR
, "invalid attempt to free string:%s", s
);
461 if (sls
->ref_count
!= 0)
463 #if SLANG_OPTIMIZE_FOR_SPEED
464 /* cache_string (sls, len, hash); */
468 #if SLANG_OPTIMIZE_FOR_SPEED
471 free_sls_string (sls
);
474 /* This routine may be passed NULL-- it is not an error. */
475 void SLang_free_slstring (char *s
)
479 #if SLANG_OPTIMIZE_FOR_SPEED
480 Cached_String_Type
*cs
;
483 if (s
== NULL
) return;
485 #if SLANG_OPTIMIZE_FOR_SPEED
486 cs
= GET_CACHED_STRING(s
);
489 SLstring_Type
*sls
= cs
->sls
;
490 if (sls
->ref_count
<= 1)
492 #if SLANG_OPTIMIZE_FOR_SPEED
494 cs
->str
= Deleted_String
;
496 free_sls_string (sls
);
504 if ((len
= strlen (s
)) < 2)
507 hash
= _pSLstring_hash ((unsigned char *)s
, (unsigned char *) s
+ len
);
508 free_long_string (s
, hash
);
511 char *SLang_create_slstring (char *s
)
514 #if SLANG_OPTIMIZE_FOR_SPEED
515 Cached_String_Type
*cs
;
518 if (s
== NULL
) return NULL
;
519 #if SLANG_OPTIMIZE_FOR_SPEED
520 cs
= GET_CACHED_STRING(s
);
523 cs
->sls
->ref_count
+= 1;
528 return create_nstring (s
, strlen (s
), &hash
);
531 void _pSLfree_hashed_string (char *s
, unsigned int len
, unsigned long hash
)
533 if ((s
== NULL
) || (len
< 2)) return;
534 free_long_string (s
, hash
);
538 char *_pSLallocate_slstring (unsigned int len
)
540 SLstring_Type
*sls
= allocate_sls (len
);
548 void _pSLunallocate_slstring (char *s
, unsigned int len
)
556 sls
= (SLstring_Type
*) (s
- offsetof(SLstring_Type
,bytes
[0]));
560 char *_pSLcreate_via_alloced_slstring (char *s
, unsigned int len
)
570 char *s1
= create_short_string (s
, len
);
571 _pSLunallocate_slstring (s
, len
);
575 /* s is not going to be in the cache because when it was malloced, its
576 * value was unknown. This simplifies the coding.
578 hash
= _pSLstring_hash ((unsigned char *)s
, (unsigned char *)s
+ len
);
579 sls
= find_string (s
, len
, hash
);
583 _pSLunallocate_slstring (s
, len
);
586 #if SLANG_OPTIMIZE_FOR_SPEED
592 sls
= (SLstring_Type
*) (s
- offsetof(SLstring_Type
,bytes
[0]));
596 #if SLANG_OPTIMIZE_FOR_SPEED
600 hash
= MAP_HASH_TO_INDEX(hash
);
601 sls
->next
= String_Hash_Table
[(unsigned int)hash
];
602 String_Hash_Table
[(unsigned int)hash
] = sls
;
607 /* Note, a and b may be ordinary strings. The result is an slstring */
608 char *SLang_concat_slstrings (char *a
, char *b
)
610 unsigned int lena
, len
;
614 len
= lena
+ strlen (b
);
616 c
= _pSLallocate_slstring (len
);
621 strcpy (c
+ lena
, b
);
623 return _pSLcreate_via_alloced_slstring (c
, len
);
626 /* This routine is assumed to work even if s is not an slstring */
627 unsigned int _pSLstring_bytelen (SLstr_Type
*s
)
629 #if SLANG_OPTIMIZE_FOR_SPEED
630 Cached_String_Type
*cs
;
632 cs
= GET_CACHED_STRING(s
);
639 /* The caller must ensure that this is an slstring */
640 void _pSLang_free_slstring (SLstr_Type
*s
)
642 #if SLANG_OPTIMIZE_FOR_SPEED
643 Cached_String_Type
*cs
;
647 if (s
== NULL
) return;
649 #if SLANG_OPTIMIZE_FOR_SPEED
650 cs
= GET_CACHED_STRING(s
);
654 if (sls
->ref_count
<= 1)
656 #if SLANG_OPTIMIZE_FOR_SPEED
658 cs
->str
= Deleted_String
;
660 free_sls_string (sls
);
668 if ((s
[0] == 0) || (s
[1] == 0))
671 sls
= (SLstring_Type
*) (s
- offsetof(SLstring_Type
,bytes
[0]));
672 if (sls
->ref_count
> 1)
677 free_long_string (s
, sls
->hash
);
680 /* An SLstring is required */
681 unsigned long _pSLstring_get_hash (SLstr_Type
*s
)
686 return _pSLstring_hash ((unsigned char*)s
, (unsigned char *)s
);
688 return _pSLstring_hash ((unsigned char *)s
, (unsigned char *)s
+1);
690 sls
= (SLstring_Type
*) (s
- offsetof(SLstring_Type
,bytes
[0]));