9 #define FORMAT_TEMP_SIZE 1024
11 #define INTERNING_POOL_SIZE 1024
12 // HASH_START_SIZE must be 2 pow
13 #define HASH_START_SIZE 16
16 struct cstring_data str
;
17 char buffer
[CSTRING_INTERNING_SIZE
];
18 struct string_node
* next
;
22 struct string_node node
[INTERNING_POOL_SIZE
];
25 struct string_interning
{
28 struct string_node
** hash
;
29 struct string_pool
* pool
;
34 static struct string_interning S
;
38 while (__sync_lock_test_and_set(&(S
.lock
),1)) {}
43 __sync_lock_release(&(S
.lock
));
47 insert_node(struct string_node
** hash
, int sz
, struct string_node
*n
) {
48 uint32_t h
= n
->str
.hash_size
;
49 int index
= h
& (sz
-1);
50 n
->next
= hash
[index
];
55 expand(struct string_interning
* si
) {
56 int new_size
= si
->size
* 2;
57 if (new_size
< HASH_START_SIZE
) {
58 new_size
= HASH_START_SIZE
;
60 assert(new_size
> si
->total
);
61 struct string_node
** new_hash
= malloc(sizeof(struct string_node
*) * new_size
);
62 memset(new_hash
, 0, sizeof(struct string_node
*) * new_size
);
64 for (i
=0;i
<si
->size
;i
++) {
65 struct string_node
*node
= si
->hash
[i
];
67 struct string_node
* tmp
= node
->next
;
68 insert_node(new_hash
, new_size
, node
);
78 interning(struct string_interning
* si
, const char * cstr
, size_t sz
, uint32_t hash
) {
79 if (si
->hash
== NULL
) {
82 int index
= (int)(hash
& (si
->size
-1));
83 struct string_node
* n
= si
->hash
[index
];
85 if (n
->str
.hash_size
== hash
) {
86 if (strcmp(n
->str
.cstr
, cstr
) == 0) {
92 // 80% (4/5) threshold
93 if (si
->total
* 5 >= si
->size
* 4) {
96 if (si
->pool
== NULL
) {
98 // todo: check memory alloc error
99 si
->pool
= malloc(sizeof(struct string_pool
));
103 n
= &si
->pool
->node
[si
->index
++];
104 memcpy(n
->buffer
, cstr
, sz
);
105 n
->buffer
[sz
] = '\0';
107 cstring cs
= &n
->str
;
108 cs
->cstr
= n
->buffer
;
109 cs
->hash_size
= hash
;
110 cs
->type
= CSTRING_INTERNING
;
113 n
->next
= si
->hash
[index
];
120 cstring_interning(const char * cstr
, size_t sz
, uint32_t hash
) {
123 ret
= interning(&S
, cstr
, sz
, hash
);
126 ret
= interning(&S
, cstr
, sz
, hash
);
136 hash_blob(const char * buffer
, size_t len
) {
137 const uint8_t * ptr
= (const uint8_t *) buffer
;
139 size_t step
= (len
>>5)+1;
141 for (i
=len
; i
>=step
; i
-=step
)
142 h
= h
^ ((h
<<5)+(h
>>2)+ptr
[i
-1]);
150 cstring_free_persist(cstring s
) {
151 if (s
->type
== CSTRING_PERMANENT
) {
157 cstring_clone(const char * cstr
, size_t sz
) {
158 if (sz
< CSTRING_INTERNING_SIZE
) {
159 return cstring_interning(cstr
, sz
, hash_blob(cstr
,sz
));
161 struct cstring_data
* p
= malloc(sizeof(struct cstring_data
) + sz
+ 1);
162 // todo: memory alloc error
164 void * ptr
= (void *)(p
+ 1);
168 memcpy(ptr
, cstr
, sz
);
169 ((char *)ptr
)[sz
] = '\0';
175 cstring_persist(const char * cstr
, size_t sz
) {
176 cstring s
= cstring_clone(cstr
, sz
);
178 s
->type
= CSTRING_PERMANENT
;
185 cstring_grab(cstring s
) {
186 if (s
->type
& (CSTRING_PERMANENT
| CSTRING_INTERNING
)) {
189 if (s
->type
== CSTRING_ONSTACK
) {
190 cstring tmp
= cstring_clone(s
->cstr
, s
->hash_size
);
194 s
->type
= CSTRING_PERMANENT
;
196 __sync_add_and_fetch(&s
->ref
,1);
203 cstring_release(cstring s
) {
210 if (__sync_sub_and_fetch(&s
->ref
,1) == 0) {
216 cstring_hash(cstring s
) {
217 if (s
->type
== CSTRING_ONSTACK
)
218 return hash_blob(s
->cstr
, s
->hash_size
);
219 if (s
->hash_size
== 0) {
220 s
->hash_size
= hash_blob(s
->cstr
, strlen(s
->cstr
));
226 cstring_equal(cstring a
, cstring b
) {
229 if ((a
->type
== CSTRING_INTERNING
) &&
230 (b
->type
== CSTRING_INTERNING
)) {
233 if ((a
->type
== CSTRING_ONSTACK
) &&
234 (b
->type
== CSTRING_ONSTACK
)) {
235 if (a
->hash_size
!= b
->hash_size
) {
238 return memcmp(a
->cstr
, b
->cstr
, a
->hash_size
) == 0;
240 uint32_t hasha
= cstring_hash(a
);
241 uint32_t hashb
= cstring_hash(b
);
242 if (hasha
!= hashb
) {
245 return strcmp(a
->cstr
, b
->cstr
) == 0;
249 cstring_cat2(const char * a
, const char * b
) {
250 size_t sa
= strlen(a
);
251 size_t sb
= strlen(b
);
252 if (sa
+ sb
< CSTRING_INTERNING_SIZE
) {
253 char tmp
[CSTRING_INTERNING_SIZE
];
255 memcpy(tmp
+sa
, b
, sb
);
257 return cstring_interning(tmp
, sa
+sb
, hash_blob(tmp
,sa
+sb
));
259 struct cstring_data
* p
= malloc(sizeof(struct cstring_data
) + sa
+ sb
+ 1);
260 // todo: memory alloc error
262 char * ptr
= (char *)(p
+ 1);
267 memcpy(ptr
+sa
, b
, sb
);
274 cstring_cat(cstring_buffer sb
, const char * str
) {
276 if (s
->type
== CSTRING_ONSTACK
) {
277 int i
= (int)s
->hash_size
;
278 while (i
< CSTRING_STACK_SIZE
-1) {
290 sb
->str
= cstring_cat2(tmp
->cstr
, str
);
291 cstring_release(tmp
);
296 cstring_format(const char * format
, va_list ap
) {
297 static char * cache
= NULL
;
300 // read cache buffer atomic
302 temp
= __sync_val_compare_and_swap(&cache
, temp
, NULL
);
305 temp
= (char *)malloc(FORMAT_TEMP_SIZE
);
306 // todo : check malloc
311 int n
= vsnprintf(temp
, FORMAT_TEMP_SIZE
, format
, ap2
);
312 if (n
>= FORMAT_TEMP_SIZE
) {
313 int sz
= FORMAT_TEMP_SIZE
* 2;
316 // todo : check malloc
319 n
= vsnprintf(result
, sz
, format
, ap2
);
330 cstring r
= (cstring
)malloc(sizeof(struct cstring_data
) + n
+ 1);
331 // todo : check malloc
333 r
->cstr
= (char *)(r
+1);
337 memcpy(r
->cstr
, result
, n
+1);
338 if (temp
!= result
) {
342 if (!__sync_bool_compare_and_swap(&cache
, NULL
, temp
)) {
351 cstring_printf(cstring_buffer sb
, const char * format
, ...) {
354 va_start(ap
, format
);
355 if (s
->type
== CSTRING_ONSTACK
) {
356 int n
= vsnprintf(s
->cstr
, CSTRING_STACK_SIZE
, format
, ap
);
357 if (n
>= CSTRING_STACK_SIZE
) {
359 va_start(ap
, format
);
360 s
= cstring_format(format
, ap
);
366 cstring_release(sb
->str
);
367 s
= cstring_format(format
, ap
);