aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/eina/src/lib/eina_stringshare.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/eina/src/lib/eina_stringshare.c')
-rw-r--r--libraries/eina/src/lib/eina_stringshare.c751
1 files changed, 751 insertions, 0 deletions
diff --git a/libraries/eina/src/lib/eina_stringshare.c b/libraries/eina/src/lib/eina_stringshare.c
new file mode 100644
index 0000000..478b300
--- /dev/null
+++ b/libraries/eina/src/lib/eina_stringshare.c
@@ -0,0 +1,751 @@
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#ifdef HAVE_CONFIG_H
26# include "config.h"
27#endif
28
29#ifdef HAVE_ALLOCA_H
30# include <alloca.h>
31#elif defined __GNUC__
32# define alloca __builtin_alloca
33#elif defined _AIX
34# define alloca __alloca
35#elif defined _MSC_VER
36# include <malloc.h>
37# define alloca _alloca
38#else
39# include <stddef.h>
40# ifdef __cplusplus
41extern "C"
42# endif
43void *alloca (size_t);
44#endif
45
46#include <stdlib.h>
47#include <stdio.h>
48#include <string.h>
49
50#ifdef HAVE_EVIL
51# include <Evil.h>
52#endif
53
54#include "eina_config.h"
55#include "eina_private.h"
56#include "eina_error.h"
57#include "eina_log.h"
58#include "eina_stringshare.h"
59#include "eina_lock.h"
60
61/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
62#include "eina_safety_checks.h"
63#include "eina_share_common.h"
64
65/* The actual share */
66static Eina_Share *stringshare_share;
67static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node";
68
69extern Eina_Bool _share_common_threads_activated;
70static Eina_Lock _mutex_small;
71
72/* Stringshare optimizations */
73static const unsigned char _eina_stringshare_single[512] = {
74 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,
75 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,
76 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,
77 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,
78 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,
79 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,
80 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,
81 105,0,
82 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,
83 0,119,0,120,0,
84 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,
85 0,134,0,135,0,
86 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,
87 0,149,0,150,0,
88 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,
89 0,164,0,165,0,
90 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,
91 0,179,0,180,0,
92 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,
93 0,194,0,195,0,
94 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,
95 0,209,0,210,0,
96 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,
97 0,224,0,225,0,
98 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,
99 0,239,0,240,0,
100 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,
101 0,254,0,255,0
102};
103
104typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small;
105typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket;
106
107struct _Eina_Stringshare_Small_Bucket
108{
109 /* separate arrays for faster lookups */
110 const char **strings;
111 unsigned char *lengths;
112 unsigned short *references;
113 int count;
114 int size;
115};
116
117struct _Eina_Stringshare_Small
118{
119 Eina_Stringshare_Small_Bucket *buckets[256];
120};
121
122#define EINA_STRINGSHARE_SMALL_BUCKET_STEP 8
123static Eina_Stringshare_Small _eina_small_share;
124
125static inline int
126_eina_stringshare_small_cmp(const Eina_Stringshare_Small_Bucket *bucket,
127 int i,
128 const char *pstr,
129 unsigned char plength)
130{
131 /* pstr and plength are from second char and on, since the first is
132 * always the same.
133 *
134 * First string being always the same, size being between 2 and 3
135 * characters (there is a check for special case length==1 and then
136 * small stringshare is applied to strings < 4), we just need to
137 * compare 2 characters of both strings.
138 */
139 const unsigned char cur_plength = bucket->lengths[i] - 1;
140 const char *cur_pstr;
141
142 if (cur_plength > plength)
143 return 1;
144 else if (cur_plength < plength)
145 return -1;
146
147 cur_pstr = bucket->strings[i] + 1;
148
149 if (cur_pstr[0] > pstr[0])
150 return 1;
151 else if (cur_pstr[0] < pstr[0])
152 return -1;
153
154 if (plength == 1)
155 return 0;
156
157 if (cur_pstr[1] > pstr[1])
158 return 1;
159 else if (cur_pstr[1] < pstr[1])
160 return -1;
161
162 return 0;
163}
164
165static const char *
166_eina_stringshare_small_bucket_find(const Eina_Stringshare_Small_Bucket *bucket,
167 const char *str,
168 unsigned char length,
169 int *idx)
170{
171 const char *pstr = str + 1; /* skip first letter, it's always the same */
172 unsigned char plength = length - 1;
173 int i, low, high;
174
175 if (bucket->count == 0)
176 {
177 *idx = 0;
178 return NULL;
179 }
180
181 low = 0;
182 high = bucket->count;
183
184 while (low < high)
185 {
186 int r;
187
188 i = (low + high - 1) / 2;
189
190 r = _eina_stringshare_small_cmp(bucket, i, pstr, plength);
191 if (r > 0)
192 high = i;
193 else if (r < 0)
194 low = i + 1;
195 else
196 {
197 *idx = i;
198 return bucket->strings[i];
199 }
200 }
201
202 *idx = low;
203 return NULL;
204}
205
206static Eina_Bool
207_eina_stringshare_small_bucket_resize(Eina_Stringshare_Small_Bucket *bucket,
208 int size)
209{
210 void *tmp;
211
212 tmp = realloc((void *)bucket->strings, size * sizeof(bucket->strings[0]));
213 if (!tmp)
214 {
215 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
216 return 0;
217 }
218
219 bucket->strings = tmp;
220
221 tmp = realloc(bucket->lengths, size * sizeof(bucket->lengths[0]));
222 if (!tmp)
223 {
224 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
225 return 0;
226 }
227
228 bucket->lengths = tmp;
229
230 tmp = realloc(bucket->references, size * sizeof(bucket->references[0]));
231 if (!tmp)
232 {
233 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
234 return 0;
235 }
236
237 bucket->references = tmp;
238
239 bucket->size = size;
240 return 1;
241}
242
243static const char *
244_eina_stringshare_small_bucket_insert_at(
245 Eina_Stringshare_Small_Bucket **p_bucket,
246 const char *str,
247 unsigned char length,
248 int idx)
249{
250 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
251 int todo, off;
252 char *snew;
253
254 if (!bucket)
255 {
256 *p_bucket = bucket = calloc(1, sizeof(*bucket));
257 if (!bucket)
258 {
259 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
260 return NULL;
261 }
262 }
263
264 if (bucket->count + 1 >= bucket->size)
265 {
266 int size = bucket->size + EINA_STRINGSHARE_SMALL_BUCKET_STEP;
267 if (!_eina_stringshare_small_bucket_resize(bucket, size))
268 return NULL;
269 }
270
271 snew = malloc(length + 1);
272 if (!snew)
273 {
274 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
275 return NULL;
276 }
277
278 memcpy(snew, str, length);
279 snew[length] = '\0';
280
281 off = idx + 1;
282 todo = bucket->count - idx;
283 if (todo > 0)
284 {
285 memmove((void *)(bucket->strings + off), bucket->strings + idx,
286 todo * sizeof(bucket->strings[0]));
287 memmove(bucket->lengths + off, bucket->lengths + idx,
288 todo * sizeof(bucket->lengths[0]));
289 memmove(bucket->references + off, bucket->references + idx,
290 todo * sizeof(bucket->references[0]));
291 }
292
293 bucket->strings[idx] = snew;
294 bucket->lengths[idx] = length;
295 bucket->references[idx] = 1;
296 bucket->count++;
297
298 return snew;
299}
300
301static void
302_eina_stringshare_small_bucket_remove_at(
303 Eina_Stringshare_Small_Bucket **p_bucket,
304 int idx)
305{
306 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
307 int todo, off;
308
309 if (bucket->references[idx] > 1)
310 {
311 bucket->references[idx]--;
312 return;
313 }
314
315 free((char *)bucket->strings[idx]);
316
317 if (bucket->count == 1)
318 {
319 free((void *)bucket->strings);
320 free(bucket->lengths);
321 free(bucket->references);
322 free(bucket);
323 *p_bucket = NULL;
324 return;
325 }
326
327 bucket->count--;
328 if (idx == bucket->count)
329 goto end;
330
331 off = idx + 1;
332 todo = bucket->count - idx;
333
334 memmove((void *)(bucket->strings + idx), bucket->strings + off,
335 todo * sizeof(bucket->strings[0]));
336 memmove(bucket->lengths + idx, bucket->lengths + off,
337 todo * sizeof(bucket->lengths[0]));
338 memmove(bucket->references + idx, bucket->references + off,
339 todo * sizeof(bucket->references[0]));
340
341end:
342 if (bucket->count + EINA_STRINGSHARE_SMALL_BUCKET_STEP < bucket->size)
343 {
344 int size = bucket->size - EINA_STRINGSHARE_SMALL_BUCKET_STEP;
345 _eina_stringshare_small_bucket_resize(bucket, size);
346 }
347}
348
349static const char *
350_eina_stringshare_small_add(const char *str, unsigned char length)
351{
352 Eina_Stringshare_Small_Bucket **bucket;
353 int i;
354
355 bucket = _eina_small_share.buckets + (unsigned char)str[0];
356 if (!*bucket)
357 i = 0;
358 else
359 {
360 const char *ret;
361 ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
362 if (ret)
363 {
364 (*bucket)->references[i]++;
365 return ret;
366 }
367 }
368
369 return _eina_stringshare_small_bucket_insert_at(bucket, str, length, i);
370}
371
372static void
373_eina_stringshare_small_del(const char *str, unsigned char length)
374{
375 Eina_Stringshare_Small_Bucket **bucket;
376 const char *ret;
377 int i;
378
379 bucket = _eina_small_share.buckets + (unsigned char)str[0];
380 if (!*bucket)
381 goto error;
382
383 ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
384 if (!ret)
385 goto error;
386
387 _eina_stringshare_small_bucket_remove_at(bucket, i);
388 return;
389
390error:
391 CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str);
392}
393
394static void
395_eina_stringshare_small_init(void)
396{
397 eina_lock_new(&_mutex_small);
398 memset(&_eina_small_share, 0, sizeof(_eina_small_share));
399}
400
401static void
402_eina_stringshare_small_shutdown(void)
403{
404 Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
405
406 p_bucket = _eina_small_share.buckets;
407 p_bucket_end = p_bucket + 256;
408
409 for (; p_bucket < p_bucket_end; p_bucket++)
410 {
411 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
412 char **s, **s_end;
413
414 if (!bucket)
415 continue;
416
417 s = (char **)bucket->strings;
418 s_end = s + bucket->count;
419 for (; s < s_end; s++)
420 free(*s);
421
422 free((void *)bucket->strings);
423 free(bucket->lengths);
424 free(bucket->references);
425 free(bucket);
426 *p_bucket = NULL;
427 }
428
429 eina_lock_free(&_mutex_small);
430}
431
432static void
433_eina_stringshare_small_bucket_dump(Eina_Stringshare_Small_Bucket *bucket,
434 struct dumpinfo *di)
435{
436 const char **s = bucket->strings;
437 unsigned char *l = bucket->lengths;
438 unsigned short *r = bucket->references;
439 int i;
440
441 di->used += sizeof(*bucket);
442 di->used += bucket->count * sizeof(*s);
443 di->used += bucket->count * sizeof(*l);
444 di->used += bucket->count * sizeof(*r);
445 di->unique += bucket->count;
446
447 for (i = 0; i < bucket->count; i++, s++, l++, r++)
448 {
449 int dups;
450#ifdef _WIN32
451 printf("DDD: %5hu %5hu '%s'\n", *l, *r, *s);
452#else
453 printf("DDD: %5hhu %5hu '%s'\n", *l, *r, *s);
454#endif
455
456 dups = (*r - 1);
457
458 di->used += *l;
459 di->saved += *l * dups;
460 di->dups += dups;
461 }
462}
463
464static void
465_eina_stringshare_small_dump(struct dumpinfo *di)
466{
467 Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
468
469 p_bucket = _eina_small_share.buckets;
470 p_bucket_end = p_bucket + 256;
471
472 for (; p_bucket < p_bucket_end; p_bucket++)
473 {
474 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
475
476 if (!bucket)
477 continue;
478
479 _eina_stringshare_small_bucket_dump(bucket, di);
480 }
481}
482
483
484/*============================================================================*
485* Global *
486*============================================================================*/
487
488/**
489 * @internal
490 * @brief Initialize the share_common module.
491 *
492 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
493 *
494 * This function sets up the share_common module of Eina. It is called by
495 * eina_init().
496 *
497 * @see eina_init()
498 */
499Eina_Bool
500eina_stringshare_init(void)
501{
502 Eina_Bool ret;
503 ret = eina_share_common_init(&stringshare_share,
504 EINA_MAGIC_STRINGSHARE_NODE,
505 EINA_MAGIC_STRINGSHARE_NODE_STR);
506 if (ret)
507 _eina_stringshare_small_init();
508
509 return ret;
510}
511
512/**
513 * @internal
514 * @brief Shut down the share_common module.
515 *
516 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
517 *
518 * This function shuts down the share_common module set up by
519 * eina_share_common_init(). It is called by eina_shutdown().
520 *
521 * @see eina_shutdown()
522 */
523Eina_Bool
524eina_stringshare_shutdown(void)
525{
526 Eina_Bool ret;
527 _eina_stringshare_small_shutdown();
528 ret = eina_share_common_shutdown(&stringshare_share);
529 return ret;
530}
531
532/*============================================================================*
533* API *
534*============================================================================*/
535
536EAPI void
537eina_stringshare_del(const char *str)
538{
539 int slen;
540
541 if (!str)
542 return;
543
544 /* special cases */
545 if (str[0] == '\0')
546 slen = 0;
547 else if (str[1] == '\0')
548 slen = 1;
549 else if (str[2] == '\0')
550 slen = 2;
551 else if (str[3] == '\0')
552 slen = 3;
553 else
554 slen = 4; /* handled later */
555
556 if (slen < 2)
557 return;
558 else if (slen < 4)
559 {
560 eina_share_common_population_del(stringshare_share, slen);
561 eina_lock_take(&_mutex_small);
562 _eina_stringshare_small_del(str, slen);
563 eina_lock_release(&_mutex_small);
564 return;
565 }
566
567 eina_share_common_del(stringshare_share, str);
568}
569
570EAPI const char *
571eina_stringshare_add_length(const char *str, unsigned int slen)
572{
573 if ((!str) || (slen <= 0))
574 return "";
575 else if (slen == 1)
576 return (const char *)_eina_stringshare_single + ((*str) << 1);
577 else if (slen < 4)
578 {
579 const char *s;
580
581 eina_lock_take(&_mutex_small);
582 s = _eina_stringshare_small_add(str, slen);
583 eina_lock_release(&_mutex_small);
584 return s;
585 }
586
587 return eina_share_common_add_length(stringshare_share, str, slen *
588 sizeof(char), sizeof(char));
589}
590
591EAPI const char *
592eina_stringshare_add(const char *str)
593{
594 int slen;
595 if (!str)
596 return NULL;
597
598 if (str[0] == '\0')
599 slen = 0;
600 else if (str[1] == '\0')
601 slen = 1;
602 else if (str[2] == '\0')
603 slen = 2;
604 else if (str[3] == '\0')
605 slen = 3;
606 else
607 slen = 3 + (int)strlen(str + 3);
608
609 return eina_stringshare_add_length(str, slen);
610}
611
612EAPI const char *
613eina_stringshare_printf(const char *fmt, ...)
614{
615 va_list args;
616 char *tmp;
617 const char *ret;
618 int len;
619
620 if (!fmt)
621 return NULL;
622
623 va_start(args, fmt);
624 len = vasprintf(&tmp, fmt, args);
625 va_end(args);
626
627 if (len < 1)
628 return NULL;
629
630 ret = eina_stringshare_add_length(tmp, len);
631 free(tmp);
632
633 return ret;
634}
635
636EAPI const char *
637eina_stringshare_vprintf(const char *fmt, va_list args)
638{
639 char *tmp;
640 const char *ret;
641 int len;
642
643 if (!fmt)
644 return NULL;
645
646 len = vasprintf(&tmp, fmt, args);
647
648 if (len < 1)
649 return NULL;
650
651 ret = eina_stringshare_add_length(tmp, len);
652 free(tmp);
653
654 return ret;
655}
656
657EAPI const char *
658eina_stringshare_nprintf(unsigned int len, const char *fmt, ...)
659{
660 va_list args;
661 char *tmp;
662 int size;
663
664 if (!fmt)
665 return NULL;
666
667 if (len < 1)
668 return NULL;
669
670 tmp = alloca(sizeof(char) * len + 1);
671
672 va_start(args, fmt);
673 size = vsnprintf(tmp, len, fmt, args);
674 va_end(args);
675
676 if (size < 1)
677 return NULL;
678
679 return eina_stringshare_add_length(tmp, len);
680}
681
682EAPI const char *
683eina_stringshare_ref(const char *str)
684{
685 int slen;
686
687 if (!str)
688 return eina_share_common_ref(stringshare_share, str);
689
690 /* special cases */
691 if (str[0] == '\0')
692 slen = 0;
693 else if (str[1] == '\0')
694 slen = 1;
695 else if (str[2] == '\0')
696 slen = 2;
697 else if (str[3] == '\0')
698 slen = 3;
699 else
700 slen = 3 + (int)strlen(str + 3);
701
702 if (slen < 2)
703 {
704 eina_share_common_population_add(stringshare_share, slen);
705
706 return str;
707 }
708 else if (slen < 4)
709 {
710 const char *s;
711 eina_share_common_population_add(stringshare_share, slen);
712
713 eina_lock_take(&_mutex_small);
714 s = _eina_stringshare_small_add(str, slen);
715 eina_lock_release(&_mutex_small);
716
717 return s;
718 }
719
720 return eina_share_common_ref(stringshare_share, str);
721}
722
723EAPI int
724eina_stringshare_strlen(const char *str)
725{
726 int len;
727 /* special cases */
728 if (str[0] == '\0')
729 return 0;
730
731 if (str[1] == '\0')
732 return 1;
733
734 if (str[2] == '\0')
735 return 2;
736
737 if (str[3] == '\0')
738 return 3;
739
740 len = eina_share_common_length(stringshare_share, (const char *)str);
741 len = (len > 0) ? len / (int)sizeof(char) : -1;
742 return len;
743}
744
745EAPI void
746eina_stringshare_dump(void)
747{
748 eina_share_common_dump(stringshare_share,
749 _eina_stringshare_small_dump,
750 sizeof(_eina_stringshare_single));
751}