File: | tests/suite/ecore/src/lib/eina_stringshare.c |
Location: | line 665, column 57 |
Description: | Dereference of null pointer loaded from variable 'str' |
1 | /* EINA - EFL data type library | ||||
2 | * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010 | ||||
3 | * Carsten Haitzler, | ||||
4 | * Jorge Luis Zapata Muga, | ||||
5 | * Cedric Bail, | ||||
6 | * Gustavo Sverzut Barbieri | ||||
7 | * Tom Hacohen | ||||
8 | * Brett Nash | ||||
9 | * | ||||
10 | * This library is free software; you can redistribute it and/or | ||||
11 | * modify it under the terms of the GNU Lesser General Public | ||||
12 | * License as published by the Free Software Foundation; either | ||||
13 | * version 2.1 of the License, or (at your option) any later version. | ||||
14 | * | ||||
15 | * This library is distributed in the hope that it will be useful, | ||||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
18 | * Lesser General Public License for more details. | ||||
19 | * | ||||
20 | * You should have received a copy of the GNU Lesser General Public | ||||
21 | * License along with this library; | ||||
22 | * if not, see <http://www.gnu.org/licenses/>. | ||||
23 | */ | ||||
24 | |||||
25 | /** | ||||
26 | * @page tutorial_stringshare_page Stringshare Tutorial | ||||
27 | * | ||||
28 | * to be written... | ||||
29 | * | ||||
30 | */ | ||||
31 | |||||
32 | #ifdef HAVE_CONFIG_H1 | ||||
33 | # include "config.h" | ||||
34 | #endif | ||||
35 | |||||
36 | #define _GNU_SOURCE | ||||
37 | |||||
38 | #ifdef HAVE_ALLOCA_H1 | ||||
39 | # include <alloca.h> | ||||
40 | #elif defined __GNUC__4 | ||||
41 | # define alloca __builtin_alloca | ||||
42 | #elif defined _AIX | ||||
43 | # define alloca __alloca | ||||
44 | #elif defined _MSC_VER | ||||
45 | # include <malloc.h> | ||||
46 | # define alloca _alloca | ||||
47 | #else | ||||
48 | # include <stddef.h> | ||||
49 | # ifdef __cplusplus | ||||
50 | extern "C" | ||||
51 | # endif | ||||
52 | void *alloca (size_t)__builtin_alloca (size_t); | ||||
53 | #endif | ||||
54 | |||||
55 | #include <stdlib.h> | ||||
56 | #include <stdio.h> | ||||
57 | #include <string.h> | ||||
58 | |||||
59 | #ifdef EFL_HAVE_POSIX_THREADS1 | ||||
60 | # include <pthread.h> | ||||
61 | #endif | ||||
62 | |||||
63 | #ifdef HAVE_EVIL | ||||
64 | # include <Evil.h> | ||||
65 | #endif | ||||
66 | |||||
67 | #include "eina_config.h" | ||||
68 | #include "eina_private.h" | ||||
69 | #include "eina_hash.h" | ||||
70 | #include "eina_rbtree.h" | ||||
71 | #include "eina_error.h" | ||||
72 | #include "eina_log.h" | ||||
73 | #include "eina_stringshare.h" | ||||
74 | |||||
75 | /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ | ||||
76 | #include "eina_safety_checks.h" | ||||
77 | #include "eina_share_common.h" | ||||
78 | |||||
79 | /* The actual share */ | ||||
80 | static Eina_Share *stringshare_share; | ||||
81 | static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node"; | ||||
82 | |||||
83 | #ifdef EFL_HAVE_THREADS1 | ||||
84 | extern Eina_Bool _share_common_threads_activated; | ||||
85 | |||||
86 | # ifdef EFL_HAVE_POSIX_THREADS1 | ||||
87 | static pthread_mutex_t _mutex_small = PTHREAD_MUTEX_INITIALIZER{ { 0, 0, 0, 0, 0, 0, { 0, 0 } } }; | ||||
88 | # define STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ) if(_share_common_threads_activated) \ | ||||
89 | pthread_mutex_lock(&_mutex_small) | ||||
90 | # define STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small) if(_share_common_threads_activated) \ | ||||
91 | pthread_mutex_unlock(&_mutex_small) | ||||
92 | # else /* EFL_HAVE_WIN32_THREADS */ | ||||
93 | static HANDLE _mutex_small = NULL((void*)0); | ||||
94 | # define STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ) if(_share_common_threads_activated) \ | ||||
95 | WaitForSingleObject(_mutex_small, INFINITE) | ||||
96 | # define STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small) if(_share_common_threads_activated) \ | ||||
97 | ReleaseMutex(_mutex_small) | ||||
98 | |||||
99 | # endif /* EFL_HAVE_WIN32_THREADS */ | ||||
100 | #else /* EFL_HAVE_THREADS */ | ||||
101 | # define STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ) do {} while (0) | ||||
102 | # define STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small) do {} while (0) | ||||
103 | #endif | ||||
104 | |||||
105 | /* Stringshare optimizations */ | ||||
106 | static const unsigned char _eina_stringshare_single[512] = { | ||||
107 | 0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0, | ||||
108 | 16,0,17,0,18,0,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0, | ||||
109 | 31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0, | ||||
110 | 46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0, | ||||
111 | 61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0, | ||||
112 | 76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0, | ||||
113 | 91,0,92,0,93,0,94,0,95,0,96,0,97,0,98,0,99,0,100,0,101,0,102,0,103,0,104,0, | ||||
114 | 105,0, | ||||
115 | 106,0,107,0,108,0,109,0,110,0,111,0,112,0,113,0,114,0,115,0,116,0,117,0,118, | ||||
116 | 0,119,0,120,0, | ||||
117 | 121,0,122,0,123,0,124,0,125,0,126,0,127,0,128,0,129,0,130,0,131,0,132,0,133, | ||||
118 | 0,134,0,135,0, | ||||
119 | 136,0,137,0,138,0,139,0,140,0,141,0,142,0,143,0,144,0,145,0,146,0,147,0,148, | ||||
120 | 0,149,0,150,0, | ||||
121 | 151,0,152,0,153,0,154,0,155,0,156,0,157,0,158,0,159,0,160,0,161,0,162,0,163, | ||||
122 | 0,164,0,165,0, | ||||
123 | 166,0,167,0,168,0,169,0,170,0,171,0,172,0,173,0,174,0,175,0,176,0,177,0,178, | ||||
124 | 0,179,0,180,0, | ||||
125 | 181,0,182,0,183,0,184,0,185,0,186,0,187,0,188,0,189,0,190,0,191,0,192,0,193, | ||||
126 | 0,194,0,195,0, | ||||
127 | 196,0,197,0,198,0,199,0,200,0,201,0,202,0,203,0,204,0,205,0,206,0,207,0,208, | ||||
128 | 0,209,0,210,0, | ||||
129 | 211,0,212,0,213,0,214,0,215,0,216,0,217,0,218,0,219,0,220,0,221,0,222,0,223, | ||||
130 | 0,224,0,225,0, | ||||
131 | 226,0,227,0,228,0,229,0,230,0,231,0,232,0,233,0,234,0,235,0,236,0,237,0,238, | ||||
132 | 0,239,0,240,0, | ||||
133 | 241,0,242,0,243,0,244,0,245,0,246,0,247,0,248,0,249,0,250,0,251,0,252,0,253, | ||||
134 | 0,254,0,255,0 | ||||
135 | }; | ||||
136 | |||||
137 | typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small; | ||||
138 | typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket; | ||||
139 | |||||
140 | struct _Eina_Stringshare_Small_Bucket | ||||
141 | { | ||||
142 | /* separate arrays for faster lookups */ | ||||
143 | const char **strings; | ||||
144 | unsigned char *lengths; | ||||
145 | unsigned short *references; | ||||
146 | int count; | ||||
147 | int size; | ||||
148 | }; | ||||
149 | |||||
150 | struct _Eina_Stringshare_Small | ||||
151 | { | ||||
152 | Eina_Stringshare_Small_Bucket *buckets[256]; | ||||
153 | }; | ||||
154 | |||||
155 | #define EINA_STRINGSHARE_SMALL_BUCKET_STEP8 8 | ||||
156 | static Eina_Stringshare_Small _eina_small_share; | ||||
157 | |||||
158 | static inline int | ||||
159 | _eina_stringshare_small_cmp(const Eina_Stringshare_Small_Bucket *bucket, | ||||
160 | int i, | ||||
161 | const char *pstr, | ||||
162 | unsigned char plength) | ||||
163 | { | ||||
164 | /* pstr and plength are from second char and on, since the first is | ||||
165 | * always the same. | ||||
166 | * | ||||
167 | * First string being always the same, size being between 2 and 3 | ||||
168 | * characters (there is a check for special case length==1 and then | ||||
169 | * small stringshare is applied to strings < 4), we just need to | ||||
170 | * compare 2 characters of both strings. | ||||
171 | */ | ||||
172 | const unsigned char cur_plength = bucket->lengths[i] - 1; | ||||
173 | const char *cur_pstr; | ||||
174 | |||||
175 | if (cur_plength > plength) | ||||
176 | return 1; | ||||
177 | else if (cur_plength < plength) | ||||
178 | return -1; | ||||
179 | |||||
180 | cur_pstr = bucket->strings[i] + 1; | ||||
181 | |||||
182 | if (cur_pstr[0] > pstr[0]) | ||||
183 | return 1; | ||||
184 | else if (cur_pstr[0] < pstr[0]) | ||||
185 | return -1; | ||||
186 | |||||
187 | if (plength == 1) | ||||
188 | return 0; | ||||
189 | |||||
190 | if (cur_pstr[1] > pstr[1]) | ||||
191 | return 1; | ||||
192 | else if (cur_pstr[1] < pstr[1]) | ||||
193 | return -1; | ||||
194 | |||||
195 | return 0; | ||||
196 | } | ||||
197 | |||||
198 | static const char * | ||||
199 | _eina_stringshare_small_bucket_find(const Eina_Stringshare_Small_Bucket *bucket, | ||||
200 | const char *str, | ||||
201 | unsigned char length, | ||||
202 | int *idx) | ||||
203 | { | ||||
204 | const char *pstr = str + 1; /* skip first letter, it's always the same */ | ||||
205 | unsigned char plength = length - 1; | ||||
206 | int i, low, high; | ||||
207 | |||||
208 | if (bucket->count == 0) | ||||
209 | { | ||||
210 | *idx = 0; | ||||
211 | return NULL((void*)0); | ||||
212 | } | ||||
213 | |||||
214 | low = 0; | ||||
215 | high = bucket->count; | ||||
216 | |||||
217 | while (low < high) | ||||
218 | { | ||||
219 | int r; | ||||
220 | |||||
221 | i = (low + high - 1) / 2; | ||||
222 | |||||
223 | r = _eina_stringshare_small_cmp(bucket, i, pstr, plength); | ||||
224 | if (r > 0) | ||||
225 | high = i; | ||||
226 | else if (r < 0) | ||||
227 | low = i + 1; | ||||
228 | else | ||||
229 | { | ||||
230 | *idx = i; | ||||
231 | return bucket->strings[i]; | ||||
232 | } | ||||
233 | } | ||||
234 | |||||
235 | *idx = low; | ||||
236 | return NULL((void*)0); | ||||
237 | } | ||||
238 | |||||
239 | static Eina_Bool | ||||
240 | _eina_stringshare_small_bucket_resize(Eina_Stringshare_Small_Bucket *bucket, | ||||
241 | int size) | ||||
242 | { | ||||
243 | void *tmp; | ||||
244 | |||||
245 | tmp = realloc((void *)bucket->strings, size * sizeof(bucket->strings[0])); | ||||
246 | if (!tmp) | ||||
247 | { | ||||
248 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||||
249 | return 0; | ||||
250 | } | ||||
251 | |||||
252 | bucket->strings = tmp; | ||||
253 | |||||
254 | tmp = realloc(bucket->lengths, size * sizeof(bucket->lengths[0])); | ||||
255 | if (!tmp) | ||||
256 | { | ||||
257 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||||
258 | return 0; | ||||
259 | } | ||||
260 | |||||
261 | bucket->lengths = tmp; | ||||
262 | |||||
263 | tmp = realloc(bucket->references, size * sizeof(bucket->references[0])); | ||||
264 | if (!tmp) | ||||
265 | { | ||||
266 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||||
267 | return 0; | ||||
268 | } | ||||
269 | |||||
270 | bucket->references = tmp; | ||||
271 | |||||
272 | bucket->size = size; | ||||
273 | return 1; | ||||
274 | } | ||||
275 | |||||
276 | static const char * | ||||
277 | _eina_stringshare_small_bucket_insert_at( | ||||
278 | Eina_Stringshare_Small_Bucket **p_bucket, | ||||
279 | const char *str, | ||||
280 | unsigned char length, | ||||
281 | int idx) | ||||
282 | { | ||||
283 | Eina_Stringshare_Small_Bucket *bucket = *p_bucket; | ||||
284 | int todo, off; | ||||
285 | char *snew; | ||||
286 | |||||
287 | if (!bucket) | ||||
288 | { | ||||
289 | *p_bucket = bucket = calloc(1, sizeof(*bucket)); | ||||
290 | if (!bucket) | ||||
291 | { | ||||
292 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||||
293 | return NULL((void*)0); | ||||
294 | } | ||||
295 | } | ||||
296 | |||||
297 | if (bucket->count + 1 >= bucket->size) | ||||
298 | { | ||||
299 | int size = bucket->size + EINA_STRINGSHARE_SMALL_BUCKET_STEP8; | ||||
300 | if (!_eina_stringshare_small_bucket_resize(bucket, size)) | ||||
301 | return NULL((void*)0); | ||||
302 | } | ||||
303 | |||||
304 | snew = malloc(length + 1); | ||||
305 | if (!snew) | ||||
306 | { | ||||
307 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||||
308 | return NULL((void*)0); | ||||
309 | } | ||||
310 | |||||
311 | memcpy(snew, str, length); | ||||
312 | snew[length] = '\0'; | ||||
313 | |||||
314 | off = idx + 1; | ||||
315 | todo = bucket->count - idx; | ||||
316 | if (todo > 0) | ||||
317 | { | ||||
318 | memmove((void *)(bucket->strings + off), bucket->strings + idx, | ||||
319 | todo * sizeof(bucket->strings[0])); | ||||
320 | memmove(bucket->lengths + off, bucket->lengths + idx, | ||||
321 | todo * sizeof(bucket->lengths[0])); | ||||
322 | memmove(bucket->references + off, bucket->references + idx, | ||||
323 | todo * sizeof(bucket->references[0])); | ||||
324 | } | ||||
325 | |||||
326 | bucket->strings[idx] = snew; | ||||
327 | bucket->lengths[idx] = length; | ||||
328 | bucket->references[idx] = 1; | ||||
329 | bucket->count++; | ||||
330 | |||||
331 | return snew; | ||||
332 | } | ||||
333 | |||||
334 | static void | ||||
335 | _eina_stringshare_small_bucket_remove_at( | ||||
336 | Eina_Stringshare_Small_Bucket **p_bucket, | ||||
337 | int idx) | ||||
338 | { | ||||
339 | Eina_Stringshare_Small_Bucket *bucket = *p_bucket; | ||||
340 | int todo, off; | ||||
341 | |||||
342 | if (bucket->references[idx] > 1) | ||||
343 | { | ||||
344 | bucket->references[idx]--; | ||||
345 | return; | ||||
346 | } | ||||
347 | |||||
348 | free((char *)bucket->strings[idx]); | ||||
349 | |||||
350 | if (bucket->count == 1) | ||||
351 | { | ||||
352 | free((void *)bucket->strings); | ||||
353 | free(bucket->lengths); | ||||
354 | free(bucket->references); | ||||
355 | free(bucket); | ||||
356 | *p_bucket = NULL((void*)0); | ||||
357 | return; | ||||
358 | } | ||||
359 | |||||
360 | bucket->count--; | ||||
361 | if (idx == bucket->count) | ||||
362 | goto end; | ||||
363 | |||||
364 | off = idx + 1; | ||||
365 | todo = bucket->count - idx; | ||||
366 | |||||
367 | memmove((void *)(bucket->strings + idx), bucket->strings + off, | ||||
368 | todo * sizeof(bucket->strings[0])); | ||||
369 | memmove(bucket->lengths + idx, bucket->lengths + off, | ||||
370 | todo * sizeof(bucket->lengths[0])); | ||||
371 | memmove(bucket->references + idx, bucket->references + off, | ||||
372 | todo * sizeof(bucket->references[0])); | ||||
373 | |||||
374 | end: | ||||
375 | if (bucket->count + EINA_STRINGSHARE_SMALL_BUCKET_STEP8 < bucket->size) | ||||
376 | { | ||||
377 | int size = bucket->size - EINA_STRINGSHARE_SMALL_BUCKET_STEP8; | ||||
378 | _eina_stringshare_small_bucket_resize(bucket, size); | ||||
379 | } | ||||
380 | } | ||||
381 | |||||
382 | static const char * | ||||
383 | _eina_stringshare_small_add(const char *str, unsigned char length) | ||||
384 | { | ||||
385 | Eina_Stringshare_Small_Bucket **bucket; | ||||
386 | int i; | ||||
387 | |||||
388 | bucket = _eina_small_share.buckets + (unsigned char)str[0]; | ||||
389 | if (!*bucket) | ||||
390 | i = 0; | ||||
391 | else | ||||
392 | { | ||||
393 | const char *ret; | ||||
394 | ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i); | ||||
395 | if (ret) | ||||
396 | { | ||||
397 | (*bucket)->references[i]++; | ||||
398 | return ret; | ||||
399 | } | ||||
400 | } | ||||
401 | |||||
402 | return _eina_stringshare_small_bucket_insert_at(bucket, str, length, i); | ||||
403 | } | ||||
404 | |||||
405 | static void | ||||
406 | _eina_stringshare_small_del(const char *str, unsigned char length) | ||||
407 | { | ||||
408 | Eina_Stringshare_Small_Bucket **bucket; | ||||
409 | const char *ret; | ||||
410 | int i; | ||||
411 | |||||
412 | bucket = _eina_small_share.buckets + (unsigned char)str[0]; | ||||
413 | if (!*bucket) | ||||
414 | goto error; | ||||
415 | |||||
416 | ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i); | ||||
417 | if (!ret) | ||||
418 | goto error; | ||||
419 | |||||
420 | _eina_stringshare_small_bucket_remove_at(bucket, i); | ||||
421 | return; | ||||
422 | |||||
423 | error: | ||||
424 | CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str)eina_log_print(_eina_share_common_log_dom, EINA_LOG_LEVEL_CRITICAL , "ecore/src/lib/eina_stringshare.c", __FUNCTION__, 424, "EEEK trying to del non-shared stringshare \"%s\"" , str); | ||||
425 | } | ||||
426 | |||||
427 | static void | ||||
428 | _eina_stringshare_small_init(void) | ||||
429 | { | ||||
430 | memset(&_eina_small_share, 0, sizeof(_eina_small_share)); | ||||
431 | } | ||||
432 | |||||
433 | static void | ||||
434 | _eina_stringshare_small_shutdown(void) | ||||
435 | { | ||||
436 | Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end; | ||||
437 | |||||
438 | p_bucket = _eina_small_share.buckets; | ||||
439 | p_bucket_end = p_bucket + 256; | ||||
440 | |||||
441 | for (; p_bucket < p_bucket_end; p_bucket++) | ||||
442 | { | ||||
443 | Eina_Stringshare_Small_Bucket *bucket = *p_bucket; | ||||
444 | char **s, **s_end; | ||||
445 | |||||
446 | if (!bucket) | ||||
447 | continue; | ||||
448 | |||||
449 | s = (char **)bucket->strings; | ||||
450 | s_end = s + bucket->count; | ||||
451 | for (; s < s_end; s++) | ||||
452 | free(*s); | ||||
453 | |||||
454 | free((void *)bucket->strings); | ||||
455 | free(bucket->lengths); | ||||
456 | free(bucket->references); | ||||
457 | free(bucket); | ||||
458 | *p_bucket = NULL((void*)0); | ||||
459 | } | ||||
460 | } | ||||
461 | |||||
462 | static void | ||||
463 | _eina_stringshare_small_bucket_dump(Eina_Stringshare_Small_Bucket *bucket, | ||||
464 | struct dumpinfo *di) | ||||
465 | { | ||||
466 | const char **s = bucket->strings; | ||||
467 | unsigned char *l = bucket->lengths; | ||||
468 | unsigned short *r = bucket->references; | ||||
469 | int i; | ||||
470 | |||||
471 | di->used += sizeof(*bucket); | ||||
472 | di->used += bucket->count * sizeof(*s); | ||||
473 | di->used += bucket->count * sizeof(*l); | ||||
474 | di->used += bucket->count * sizeof(*r); | ||||
475 | di->unique += bucket->count; | ||||
476 | |||||
477 | for (i = 0; i < bucket->count; i++, s++, l++, r++) | ||||
478 | { | ||||
479 | int dups; | ||||
480 | #ifdef _WIN32 | ||||
481 | printf("DDD: %5hu %5hu '%s'\n", *l, *r, *s); | ||||
482 | #else | ||||
483 | printf("DDD: %5hhu %5hu '%s'\n", *l, *r, *s); | ||||
484 | #endif | ||||
485 | |||||
486 | dups = (*r - 1); | ||||
487 | |||||
488 | di->used += *l; | ||||
489 | di->saved += *l * dups; | ||||
490 | di->dups += dups; | ||||
491 | } | ||||
492 | } | ||||
493 | |||||
494 | static void | ||||
495 | _eina_stringshare_small_dump(struct dumpinfo *di) | ||||
496 | { | ||||
497 | Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end; | ||||
498 | |||||
499 | p_bucket = _eina_small_share.buckets; | ||||
500 | p_bucket_end = p_bucket + 256; | ||||
501 | |||||
502 | for (; p_bucket < p_bucket_end; p_bucket++) | ||||
503 | { | ||||
504 | Eina_Stringshare_Small_Bucket *bucket = *p_bucket; | ||||
505 | |||||
506 | if (!bucket) | ||||
507 | continue; | ||||
508 | |||||
509 | _eina_stringshare_small_bucket_dump(bucket, di); | ||||
510 | } | ||||
511 | } | ||||
512 | |||||
513 | |||||
514 | /*============================================================================* | ||||
515 | * Global * | ||||
516 | *============================================================================*/ | ||||
517 | |||||
518 | /** | ||||
519 | * @internal | ||||
520 | * @brief Initialize the share_common module. | ||||
521 | * | ||||
522 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||||
523 | * | ||||
524 | * This function sets up the share_common module of Eina. It is called by | ||||
525 | * eina_init(). | ||||
526 | * | ||||
527 | * @see eina_init() | ||||
528 | */ | ||||
529 | Eina_Bool | ||||
530 | eina_stringshare_init(void) | ||||
531 | { | ||||
532 | Eina_Bool ret; | ||||
533 | ret = eina_share_common_init(&stringshare_share, | ||||
534 | EINA_MAGIC_STRINGSHARE_NODE0x98761254, | ||||
535 | EINA_MAGIC_STRINGSHARE_NODE_STR); | ||||
536 | if (ret) | ||||
537 | _eina_stringshare_small_init(); | ||||
538 | |||||
539 | return ret; | ||||
540 | } | ||||
541 | |||||
542 | /** | ||||
543 | * @internal | ||||
544 | * @brief Shut down the share_common module. | ||||
545 | * | ||||
546 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||||
547 | * | ||||
548 | * This function shuts down the share_common module set up by | ||||
549 | * eina_share_common_init(). It is called by eina_shutdown(). | ||||
550 | * | ||||
551 | * @see eina_shutdown() | ||||
552 | */ | ||||
553 | Eina_Bool | ||||
554 | eina_stringshare_shutdown(void) | ||||
555 | { | ||||
556 | Eina_Bool ret; | ||||
557 | _eina_stringshare_small_shutdown(); | ||||
558 | ret = eina_share_common_shutdown(&stringshare_share); | ||||
559 | return ret; | ||||
560 | } | ||||
561 | |||||
562 | /*============================================================================* | ||||
563 | * API * | ||||
564 | *============================================================================*/ | ||||
565 | |||||
566 | /** | ||||
567 | * @addtogroup Eina_Stringshare_Group Stringshare | ||||
568 | * | ||||
569 | * These functions allow you to store one copy of a string, and use it | ||||
570 | * throughout your program. | ||||
571 | * | ||||
572 | * This is a method to reduce the number of duplicated strings kept in | ||||
573 | * memory. It's pretty common for the same strings to be dynamically | ||||
574 | * allocated repeatedly between applications and libraries, especially in | ||||
575 | * circumstances where you could have multiple copies of a structure that | ||||
576 | * allocates the string. So rather than duplicating and freeing these | ||||
577 | * strings, you request a read-only pointer to an existing string and | ||||
578 | * only incur the overhead of a hash lookup. | ||||
579 | * | ||||
580 | * It sounds like micro-optimizing, but profiling has shown this can have | ||||
581 | * a significant impact as you scale the number of copies up. It improves | ||||
582 | * string creation/destruction speed, reduces memory use and decreases | ||||
583 | * memory fragmentation, so a win all-around. | ||||
584 | * | ||||
585 | * For more information, you can look at the @ref tutorial_stringshare_page. | ||||
586 | * | ||||
587 | * @{ | ||||
588 | */ | ||||
589 | |||||
590 | /** | ||||
591 | * @brief Note that the given string has lost an instance. | ||||
592 | * | ||||
593 | * @param str string The given string. | ||||
594 | * | ||||
595 | * This function decreases the reference counter associated to @p str | ||||
596 | * if it exists. If that counter reaches 0, the memory associated to | ||||
597 | * @p str is freed. If @p str is NULL, the function returns | ||||
598 | * immediately. | ||||
599 | * | ||||
600 | * Note that if the given pointer is not shared or NULL, bad things | ||||
601 | * will happen, likely a segmentation fault. | ||||
602 | */ | ||||
603 | EAPI__attribute__ ((visibility("default"))) void | ||||
604 | eina_stringshare_del(const char *str) | ||||
605 | { | ||||
606 | int slen; | ||||
607 | DBG("str=%p (%s)", str, str ? str : "")eina_log_print(_eina_share_common_log_dom, EINA_LOG_LEVEL_DBG , "ecore/src/lib/eina_stringshare.c", __FUNCTION__, 607, "str=%p (%s)" , str, str ? str : ""); | ||||
608 | if (!str) | ||||
609 | return; | ||||
610 | |||||
611 | /* special cases */ | ||||
612 | if (str[0] == '\0') | ||||
613 | slen = 0; | ||||
614 | else if (str[1] == '\0') | ||||
615 | slen = 1; | ||||
616 | else if (str[2] == '\0') | ||||
617 | slen = 2; | ||||
618 | else if (str[3] == '\0') | ||||
619 | slen = 3; | ||||
620 | else | ||||
621 | slen = 4; /* handled later */ | ||||
622 | |||||
623 | if (slen < 2) | ||||
624 | return; | ||||
625 | else if (slen < 4) | ||||
626 | { | ||||
627 | eina_share_common_population_del(stringshare_share, slen); | ||||
628 | STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ); | ||||
629 | _eina_stringshare_small_del(str, slen); | ||||
630 | STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small); | ||||
631 | return; | ||||
632 | } | ||||
633 | |||||
634 | eina_share_common_del(stringshare_share, str); | ||||
635 | } | ||||
636 | |||||
637 | /** | ||||
638 | * @brief Retrieve an instance of a string for use in a program. | ||||
639 | * | ||||
640 | * @param str The string to retrieve an instance of. | ||||
641 | * @param slen The string size (<= strlen(str)). | ||||
642 | * @return A pointer to an instance of the string on success. | ||||
643 | * @c NULL on failure. | ||||
644 | * | ||||
645 | * This function retrieves an instance of @p str. If @p str is | ||||
646 | * @c NULL, then @c NULL is returned. If @p str is already stored, it | ||||
647 | * is just returned and its reference counter is increased. Otherwise | ||||
648 | * it is added to the strings to be searched and a duplicated string | ||||
649 | * of @p str is returned. | ||||
650 | * | ||||
651 | * This function does not check string size, but uses the | ||||
652 | * exact given size. This can be used to share_common part of a larger | ||||
653 | * buffer or substring. | ||||
654 | * | ||||
655 | * @see eina_share_common_add() | ||||
656 | */ | ||||
657 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
658 | eina_stringshare_add_length(const char *str, unsigned int slen) | ||||
659 | { | ||||
660 | DBG("str=%p (%.*s), slen=%u", str, slen, str ? str : "", slen)eina_log_print(_eina_share_common_log_dom, EINA_LOG_LEVEL_DBG , "ecore/src/lib/eina_stringshare.c", __FUNCTION__, 660, "str=%p (%.*s), slen=%u" , str, slen, str ? str : "", slen); | ||||
| |||||
661 | |||||
662 | if (slen <= 0) | ||||
| |||||
663 | return ""; | ||||
664 | else if (slen == 1) | ||||
| |||||
665 | return (const char *)_eina_stringshare_single + ((*str) << 1); | ||||
| |||||
666 | else if (slen < 4) | ||||
667 | { | ||||
668 | const char *s; | ||||
669 | |||||
670 | STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ); | ||||
671 | s = _eina_stringshare_small_add(str, slen); | ||||
672 | STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small); | ||||
673 | return s; | ||||
674 | } | ||||
675 | |||||
676 | return eina_share_common_add_length(stringshare_share, str, slen * | ||||
677 | sizeof(char), sizeof(char)); | ||||
678 | } | ||||
679 | |||||
680 | /** | ||||
681 | * @brief Retrieve an instance of a string for use in a program. | ||||
682 | * | ||||
683 | * @param str The NULL terminated string to retrieve an instance of. | ||||
684 | * @return A pointer to an instance of the string on success. | ||||
685 | * @c NULL on failure. | ||||
686 | * | ||||
687 | * This function retrieves an instance of @p str. If @p str is | ||||
688 | * @c NULL, then @c NULL is returned. If @p str is already stored, it | ||||
689 | * is just returned and its reference counter is increased. Otherwise | ||||
690 | * it is added to the strings to be searched and a duplicated string | ||||
691 | * of @p str is returned. | ||||
692 | * | ||||
693 | * The string @p str must be NULL terminated ('@\0') and its full | ||||
694 | * length will be used. To use part of the string or non-null | ||||
695 | * terminated, use eina_stringshare_add_length() instead. | ||||
696 | * | ||||
697 | * @see eina_stringshare_add_length() | ||||
698 | */ | ||||
699 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
700 | eina_stringshare_add(const char *str) | ||||
701 | { | ||||
702 | int slen; | ||||
703 | if (!str) | ||||
704 | return NULL((void*)0); | ||||
705 | |||||
706 | if (str[0] == '\0') | ||||
707 | slen = 0; | ||||
708 | else if (str[1] == '\0') | ||||
709 | slen = 1; | ||||
710 | else if (str[2] == '\0') | ||||
711 | slen = 2; | ||||
712 | else if (str[3] == '\0') | ||||
713 | slen = 3; | ||||
714 | else | ||||
715 | slen = 3 + (int)strlen(str + 3); | ||||
716 | |||||
717 | return eina_stringshare_add_length(str, slen); | ||||
718 | } | ||||
719 | |||||
720 | /** | ||||
721 | * @brief Retrieve an instance of a string for use in a program | ||||
722 | * from a format string. | ||||
723 | * | ||||
724 | * @param fmt The NULL terminated format string to retrieve an instance of. | ||||
725 | * @return A pointer to an instance of the string on success. | ||||
726 | * @c NULL on failure. | ||||
727 | * | ||||
728 | * This function retrieves an instance of @p fmt. If @p fmt is | ||||
729 | * @c NULL, then @c NULL is returned. If @p fmt is already stored, it | ||||
730 | * is just returned and its reference counter is increased. Otherwise | ||||
731 | * it is added to the strings to be searched and a duplicated string | ||||
732 | * is returned. | ||||
733 | * | ||||
734 | * The format string @p fmt must be NULL terminated ('@\0') and its full | ||||
735 | * length will be used. To use part of the format string or non-null | ||||
736 | * terminated, use eina_stringshare_nprintf() instead. | ||||
737 | * | ||||
738 | * @see eina_stringshare_nprintf() | ||||
739 | */ | ||||
740 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
741 | eina_stringshare_printf(const char *fmt, ...) | ||||
742 | { | ||||
743 | va_list args; | ||||
744 | char *tmp; | ||||
745 | const char *ret; | ||||
746 | int len; | ||||
747 | |||||
748 | if (!fmt) | ||||
749 | return NULL((void*)0); | ||||
750 | |||||
751 | va_start(args, fmt)__builtin_va_start(args, fmt); | ||||
752 | len = vasprintf(&tmp, fmt, args); | ||||
753 | va_end(args)__builtin_va_end(args); | ||||
754 | |||||
755 | if (len < 1) | ||||
756 | return NULL((void*)0); | ||||
757 | |||||
758 | ret = eina_stringshare_add_length(tmp, len); | ||||
759 | free(tmp); | ||||
760 | |||||
761 | return ret; | ||||
762 | } | ||||
763 | |||||
764 | /** | ||||
765 | * @brief Retrieve an instance of a string for use in a program | ||||
766 | * from a format string. | ||||
767 | * | ||||
768 | * @param fmt The NULL terminated format string to retrieve an instance of. | ||||
769 | * @param args The va_args for @p fmt | ||||
770 | * @return A pointer to an instance of the string on success. | ||||
771 | * @c NULL on failure. | ||||
772 | * | ||||
773 | * This function retrieves an instance of @p fmt with @p args. If @p fmt is | ||||
774 | * @c NULL, then @c NULL is returned. If @p fmt with @p args is already stored, it | ||||
775 | * is just returned and its reference counter is increased. Otherwise | ||||
776 | * it is added to the strings to be searched and a duplicated string | ||||
777 | * is returned. | ||||
778 | * | ||||
779 | * The format string @p fmt must be NULL terminated ('@\0') and its full | ||||
780 | * length will be used. To use part of the format string or non-null | ||||
781 | * terminated, use eina_stringshare_nprintf() instead. | ||||
782 | * | ||||
783 | * @see eina_stringshare_nprintf() | ||||
784 | */ | ||||
785 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
786 | eina_stringshare_vprintf(const char *fmt, va_list args) | ||||
787 | { | ||||
788 | char *tmp; | ||||
789 | const char *ret; | ||||
790 | int len; | ||||
791 | |||||
792 | if (!fmt) | ||||
793 | return NULL((void*)0); | ||||
794 | |||||
795 | len = vasprintf(&tmp, fmt, args); | ||||
796 | |||||
797 | if (len < 1) | ||||
798 | return NULL((void*)0); | ||||
799 | |||||
800 | ret = eina_stringshare_add_length(tmp, len); | ||||
801 | free(tmp); | ||||
802 | |||||
803 | return ret; | ||||
804 | } | ||||
805 | |||||
806 | /** | ||||
807 | * @brief Retrieve an instance of a string for use in a program | ||||
808 | * from a format string with size limitation. | ||||
809 | * @param len The length of the format string to use | ||||
810 | * @param fmt The format string to retrieve an instance of. | ||||
811 | * @return A pointer to an instance of the string on success. | ||||
812 | * @c NULL on failure. | ||||
813 | * | ||||
814 | * This function retrieves an instance of @p fmt limited by @p len. If @p fmt is | ||||
815 | * @c NULL or @p len is < 1, then @c NULL is returned. If the resulting string | ||||
816 | * is already stored, it is returned and its reference counter is increased. Otherwise | ||||
817 | * it is added to the strings to be searched and a duplicated string | ||||
818 | * is returned. | ||||
819 | * | ||||
820 | * @p len length of the format string will be used. To use the | ||||
821 | * entire format string, use eina_stringshare_printf() instead. | ||||
822 | * | ||||
823 | * @see eina_stringshare_printf() | ||||
824 | */ | ||||
825 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
826 | eina_stringshare_nprintf(unsigned int len, const char *fmt, ...) | ||||
827 | { | ||||
828 | va_list args; | ||||
829 | char *tmp; | ||||
830 | int size; | ||||
831 | |||||
832 | if (!fmt) | ||||
833 | return NULL((void*)0); | ||||
834 | |||||
835 | if (len < 1) | ||||
836 | return NULL((void*)0); | ||||
837 | |||||
838 | tmp = alloca(sizeof(char) * len + 1)__builtin_alloca (sizeof(char) * len + 1); | ||||
839 | |||||
840 | va_start(args, fmt)__builtin_va_start(args, fmt); | ||||
841 | size = vsnprintf(tmp, len, fmt, args); | ||||
842 | va_end(args)__builtin_va_end(args); | ||||
843 | |||||
844 | if (size < 1) | ||||
845 | return NULL((void*)0); | ||||
846 | |||||
847 | return eina_stringshare_add_length(tmp, len); | ||||
848 | } | ||||
849 | |||||
850 | /** | ||||
851 | * Increment references of the given shared string. | ||||
852 | * | ||||
853 | * @param str The shared string. | ||||
854 | * @return A pointer to an instance of the string on success. | ||||
855 | * @c NULL on failure. | ||||
856 | * | ||||
857 | * This is similar to eina_share_common_add(), but it's faster since it will | ||||
858 | * avoid lookups if possible, but on the down side it requires the parameter | ||||
859 | * to be shared before, in other words, it must be the return of a previous | ||||
860 | * eina_share_common_add(). | ||||
861 | * | ||||
862 | * There is no unref since this is the work of eina_share_common_del(). | ||||
863 | */ | ||||
864 | EAPI__attribute__ ((visibility("default"))) const char * | ||||
865 | eina_stringshare_ref(const char *str) | ||||
866 | { | ||||
867 | int slen; | ||||
868 | DBG("str=%p (%s)", str, str ? str : "")eina_log_print(_eina_share_common_log_dom, EINA_LOG_LEVEL_DBG , "ecore/src/lib/eina_stringshare.c", __FUNCTION__, 868, "str=%p (%s)" , str, str ? str : ""); | ||||
869 | |||||
870 | if (!str) | ||||
871 | return eina_share_common_ref(stringshare_share, str); | ||||
872 | |||||
873 | /* special cases */ | ||||
874 | if (str[0] == '\0') | ||||
875 | slen = 0; | ||||
876 | else if (str[1] == '\0') | ||||
877 | slen = 1; | ||||
878 | else if (str[2] == '\0') | ||||
879 | slen = 2; | ||||
880 | else if (str[3] == '\0') | ||||
881 | slen = 3; | ||||
882 | else | ||||
883 | slen = 3 + (int)strlen(str + 3); | ||||
884 | |||||
885 | if (slen < 2) | ||||
886 | { | ||||
887 | eina_share_common_population_add(stringshare_share, slen); | ||||
888 | |||||
889 | return str; | ||||
890 | } | ||||
891 | else if (slen < 4) | ||||
892 | { | ||||
893 | const char *s; | ||||
894 | eina_share_common_population_add(stringshare_share, slen); | ||||
895 | |||||
896 | STRINGSHARE_LOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_lock(&_mutex_small ); | ||||
897 | s = _eina_stringshare_small_add(str, slen); | ||||
898 | STRINGSHARE_UNLOCK_SMALL()if(_share_common_threads_activated) pthread_mutex_unlock(& _mutex_small); | ||||
899 | |||||
900 | return s; | ||||
901 | } | ||||
902 | |||||
903 | return eina_share_common_ref(stringshare_share, str); | ||||
904 | } | ||||
905 | |||||
906 | /** | ||||
907 | * @brief Note that the given string @b must be shared. | ||||
908 | * | ||||
909 | * @param str the shared string to know the length. It is safe to | ||||
910 | * give NULL, in that case -1 is returned. | ||||
911 | * | ||||
912 | * This function is a cheap way to known the length of a shared | ||||
913 | * string. Note that if the given pointer is not shared, bad | ||||
914 | * things will happen, likely a segmentation fault. If in doubt, try | ||||
915 | * strlen(). | ||||
916 | */ | ||||
917 | EAPI__attribute__ ((visibility("default"))) int | ||||
918 | eina_stringshare_strlen(const char *str) | ||||
919 | { | ||||
920 | int len; | ||||
921 | /* special cases */ | ||||
922 | if (str[0] == '\0') | ||||
923 | return 0; | ||||
924 | |||||
925 | if (str[1] == '\0') | ||||
926 | return 1; | ||||
927 | |||||
928 | if (str[2] == '\0') | ||||
929 | return 2; | ||||
930 | |||||
931 | if (str[3] == '\0') | ||||
932 | return 3; | ||||
933 | |||||
934 | len = eina_share_common_length(stringshare_share, (const char *)str); | ||||
935 | len = (len > 0) ? len / (int)sizeof(char) : -1; | ||||
936 | return len; | ||||
937 | } | ||||
938 | |||||
939 | /** | ||||
940 | * @brief Dump the contents of the share_common. | ||||
941 | * | ||||
942 | * This function dumps all strings in the share_common to stdout with a | ||||
943 | * DDD: prefix per line and a memory usage summary. | ||||
944 | */ | ||||
945 | EAPI__attribute__ ((visibility("default"))) void | ||||
946 | eina_stringshare_dump(void) | ||||
947 | { | ||||
948 | eina_share_common_dump(stringshare_share, | ||||
949 | _eina_stringshare_small_dump, | ||||
950 | sizeof(_eina_stringshare_single)); | ||||
951 | } | ||||
952 | |||||
953 | /** | ||||
954 | * @} | ||||
955 | */ | ||||
956 |