Wapcaplet
Loading...
Searching...
No Matches
libwapcaplet.c
Go to the documentation of this file.
1/* libwapcaplet.c
2 *
3 * String internment and management tools.
4 *
5 * Copyright 2009 The NetSurf Browser Project.
6 * Daniel Silverstone <dsilvers@netsurf-browser.org>
7 */
8
9#include <stdlib.h>
10#include <string.h>
11#include <assert.h>
12
14
15#ifndef UNUSED
16#define UNUSED(x) ((x) = (x))
17#endif
18
19static inline lwc_hash
20lwc__calculate_hash(const char *str, size_t len)
21{
22 lwc_hash z = 0x811c9dc5;
23
24 while (len > 0) {
25 z *= 0x01000193;
26 z ^= *str++;
27 len--;
28 }
29
30 return z;
31}
32
33#define STR_OF(str) ((char *)(str + 1))
34#define CSTR_OF(str) ((const char *)(str + 1))
35
36#define NR_BUCKETS_DEFAULT (4091)
37
42
43static lwc_context *ctx = NULL;
44
45#define LWC_ALLOC(s) malloc(s)
46#define LWC_FREE(p) free(p)
47
48typedef lwc_hash (*lwc_hasher)(const char *, size_t);
49typedef int (*lwc_strncmp)(const char *, const char *, size_t);
50typedef void * (*lwc_memcpy)(void * restrict, const void * restrict, size_t);
51
52static lwc_error
53lwc__initialise(void)
54{
55 if (ctx != NULL)
56 return lwc_error_ok;
57
58 ctx = LWC_ALLOC(sizeof(lwc_context));
59
60 if (ctx == NULL)
61 return lwc_error_oom;
62
63 memset(ctx, 0, sizeof(lwc_context));
64
66 ctx->buckets = LWC_ALLOC(sizeof(lwc_string *) * ctx->bucketcount);
67
68 if (ctx->buckets == NULL) {
69 LWC_FREE(ctx);
70 ctx = NULL;
71 return lwc_error_oom;
72 }
73
74 memset(ctx->buckets, 0, sizeof(lwc_string *) * ctx->bucketcount);
75
76 return lwc_error_ok;
77}
78
79static lwc_error
80lwc__intern(const char *s, size_t slen,
81 lwc_string **ret,
82 lwc_hasher hasher,
83 lwc_strncmp compare,
84 lwc_memcpy copy)
85{
86 lwc_hash h;
87 lwc_hash bucket;
88 lwc_string *str;
89 lwc_error eret;
90
91 assert((s != NULL) || (slen == 0));
92 assert(ret);
93
94 if (ctx == NULL) {
95 eret = lwc__initialise();
96 if (eret != lwc_error_ok)
97 return eret;
98 if (ctx == NULL)
99 return lwc_error_oom;
100 }
101
102 h = hasher(s, slen);
103 bucket = h % ctx->bucketcount;
104 str = ctx->buckets[bucket];
105
106 while (str != NULL) {
107 if ((str->hash == h) && (str->len == slen)) {
108 if (compare(CSTR_OF(str), s, slen) == 0) {
109 str->refcnt++;
110 *ret = str;
111 return lwc_error_ok;
112 }
113 }
114 str = str->next;
115 }
116
117 /* Add one for the additional NUL. */
118 *ret = str = LWC_ALLOC(sizeof(lwc_string) + slen + 1);
119
120 if (str == NULL)
121 return lwc_error_oom;
122
123 str->prevptr = &(ctx->buckets[bucket]);
124 str->next = ctx->buckets[bucket];
125 if (str->next != NULL)
126 str->next->prevptr = &(str->next);
127 ctx->buckets[bucket] = str;
128
129 str->len = slen;
130 str->hash = h;
131 str->refcnt = 1;
132 str->insensitive = NULL;
133
134 copy(STR_OF(str), s, slen);
135
136 /* Guarantee NUL termination */
137 STR_OF(str)[slen] = '\0';
138
139 return lwc_error_ok;
140}
141
143lwc_intern_string(const char *s, size_t slen,
144 lwc_string **ret)
145{
146 return lwc__intern(s, slen, ret,
147 lwc__calculate_hash,
148 strncmp, (lwc_memcpy)memcpy);
149}
150
153 size_t ssoffset, size_t sslen,
154 lwc_string **ret)
155{
156 assert(str);
157 assert(ret);
158
159 if (ssoffset >= str->len)
160 return lwc_error_range;
161 if ((ssoffset + sslen) > str->len)
162 return lwc_error_range;
163
164 return lwc_intern_string(CSTR_OF(str) + ssoffset, sslen, ret);
165}
166
169{
170 assert(str);
171 assert(ret);
172
173 /* Internally make use of knowledge that insensitive strings
174 * are lower case. */
175 if (str->insensitive == NULL) {
177 if (error != lwc_error_ok) {
178 return error;
179 }
180 }
181
182 *ret = lwc_string_ref(str->insensitive);
183 return lwc_error_ok;
184}
185
186void
188{
189 assert(str);
190
191 *(str->prevptr) = str->next;
192
193 if (str->next != NULL)
194 str->next->prevptr = str->prevptr;
195
196 if (str->insensitive != NULL && str->refcnt == 0)
198
199#ifndef NDEBUG
200 memset(str, 0xA5, sizeof(*str) + str->len);
201#endif
202
203 LWC_FREE(str);
204}
205
206/**** Shonky caseless bits ****/
207
208static inline char
209lwc__dolower(const char c)
210{
211 if (c >= 'A' && c <= 'Z')
212 return c + 'a' - 'A';
213 return c;
214}
215
216static inline lwc_hash
217lwc__calculate_lcase_hash(const char *str, size_t len)
218{
219 lwc_hash z = 0x811c9dc5;
220
221 while (len > 0) {
222 z *= 0x01000193;
223 z ^= lwc__dolower(*str++);
224 len--;
225 }
226
227 return z;
228}
229
230static int
231lwc__lcase_strncmp(const char *s1, const char *s2, size_t n)
232{
233 while (n--) {
234 if (*s1++ != lwc__dolower(*s2++))
236 return 1;
237 }
238 return 0;
239}
240
241static void *
242lwc__lcase_memcpy(void *restrict _target, const void *restrict _source, size_t n)
243{
244 char *restrict target = _target;
245 const char *restrict source = _source;
246
247 while (n--) {
248 *target++ = lwc__dolower(*source++);
249 }
250
251 return _target;
252}
253
256{
257 assert(str);
258 assert(str->insensitive == NULL);
259
260 return lwc__intern(CSTR_OF(str),
261 str->len, &(str->insensitive),
262 lwc__calculate_lcase_hash,
263 lwc__lcase_strncmp,
264 lwc__lcase_memcpy);
265}
266
267/**** Iteration ****/
268
269void
271{
272 lwc_hash n;
273 lwc_string *str;
274 bool found = false;
275
276 if (ctx == NULL)
277 return;
278
279 for (n = 0; n < ctx->bucketcount; ++n) {
280 for (str = ctx->buckets[n]; str != NULL; str = str->next) {
281 found = true;
282 cb(str, pw);
283 }
284 }
285
286 if (found == false) {
287 /* We found no strings, so remove the global context. */
288 free(ctx->buckets);
289 free(ctx);
290 ctx = NULL;
291 }
292}
lwc_error lwc_string_tolower(lwc_string *str, lwc_string **ret)
lwc_error lwc_intern_substring(lwc_string *str, size_t ssoffset, size_t sslen, lwc_string **ret)
lwc_error lwc_intern_string(const char *s, size_t slen, lwc_string **ret)
#define LWC_ALLOC(s)
#define STR_OF(str)
void *(* lwc_memcpy)(void *restrict, const void *restrict, size_t)
#define NR_BUCKETS_DEFAULT
void lwc_string_destroy(lwc_string *str)
lwc_hash(* lwc_hasher)(const char *, size_t)
void lwc_iterate_strings(lwc_iteration_callback_fn cb, void *pw)
#define CSTR_OF(str)
lwc_error lwc__intern_caseless_string(lwc_string *str)
struct lwc_context_s lwc_context
#define LWC_FREE(p)
int(* lwc_strncmp)(const char *, const char *, size_t)
uint32_t lwc_hash
#define lwc_string_unref(str)
enum lwc_error_e lwc_error
void(* lwc_iteration_callback_fn)(lwc_string *str, void *pw)
@ lwc_error_ok
@ lwc_error_oom
@ lwc_error_range
lwc_hash bucketcount
lwc_string ** buckets
struct lwc_string_s * next
lwc_refcounter refcnt
struct lwc_string_s * insensitive
lwc_hash hash
struct lwc_string_s ** prevptr