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