1 /* EINA - EFL data type library
2 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010
4 * Jorge Luis Zapata Muga,
6 * Gustavo Sverzut Barbieri
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.
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.
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/>.
37 #include "eina_config.h"
38 #include "eina_private.h"
39 #include "eina_alloca.h"
41 #include "eina_lock.h"
42 #include "eina_share_common.h"
44 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
45 #include "eina_safety_checks.h"
46 #include "eina_stringshare.h"
52 #define CRI(...) EINA_LOG_DOM_CRIT(_eina_share_stringshare_log_dom, __VA_ARGS__)
57 #define ERR(...) EINA_LOG_DOM_ERR(_eina_share_stringshare_log_dom, __VA_ARGS__)
62 #define DBG(...) EINA_LOG_DOM_DBG(_eina_share_stringshare_log_dom, __VA_ARGS__)
64 static int _eina_share_stringshare_log_dom = -1;
66 /* The actual share */
67 static Eina_Share *stringshare_share;
68 static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node";
70 extern Eina_Bool _share_common_threads_activated;
71 static Eina_Spinlock _mutex_small;
73 /* Stringshare optimizations */
74 static const unsigned char _eina_stringshare_single[512] = {
75 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,
76 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,
77 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,
78 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,
79 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,
80 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,
81 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,
83 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,
85 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,
87 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,
89 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,
91 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,
93 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,
95 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,
97 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,
99 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,
101 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,
105 typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small;
106 typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket;
108 struct _Eina_Stringshare_Small_Bucket
110 /* separate arrays for faster lookups */
111 const char **strings;
112 unsigned char *lengths;
113 unsigned int *references;
118 struct _Eina_Stringshare_Small
120 Eina_Stringshare_Small_Bucket *buckets[256];
123 #define EINA_STRINGSHARE_SMALL_BUCKET_STEP 8
124 static Eina_Stringshare_Small _eina_small_share;
127 _eina_stringshare_small_cmp(const Eina_Stringshare_Small_Bucket *bucket,
130 unsigned char plength)
132 /* pstr and plength are from second char and on, since the first is
135 * First string being always the same, size being between 2 and 3
136 * characters (there is a check for special case length==1 and then
137 * small stringshare is applied to strings < 4), we just need to
138 * compare 2 characters of both strings.
140 const unsigned char cur_plength = bucket->lengths[i] - 1;
141 const char *cur_pstr;
143 if (cur_plength > plength)
145 else if (cur_plength < plength)
148 cur_pstr = bucket->strings[i] + 1;
150 if (cur_pstr[0] > pstr[0])
152 else if (cur_pstr[0] < pstr[0])
158 if (cur_pstr[1] > pstr[1])
160 else if (cur_pstr[1] < pstr[1])
167 _eina_stringshare_small_bucket_find(const Eina_Stringshare_Small_Bucket *bucket,
169 unsigned char length,
172 const char *pstr = str + 1; /* skip first letter, it's always the same */
173 unsigned char plength = length - 1;
176 if (bucket->count == 0)
183 high = bucket->count;
189 i = (low + high - 1) / 2;
191 r = _eina_stringshare_small_cmp(bucket, i, pstr, plength);
199 return bucket->strings[i];
208 _eina_stringshare_small_bucket_resize(Eina_Stringshare_Small_Bucket *bucket,
213 tmp = realloc((void *)bucket->strings, size * sizeof(bucket->strings[0]));
215 bucket->strings = tmp;
217 tmp = realloc(bucket->lengths, size * sizeof(bucket->lengths[0]));
219 bucket->lengths = tmp;
221 tmp = realloc(bucket->references, size * sizeof(bucket->references[0]));
223 bucket->references = tmp;
230 _eina_stringshare_small_bucket_insert_at(
231 Eina_Stringshare_Small_Bucket **p_bucket,
233 unsigned char length,
236 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
242 *p_bucket = bucket = calloc(1, sizeof(*bucket));
243 if (!bucket) return NULL;
246 if (bucket->count + 1 >= bucket->size)
248 int size = bucket->size + EINA_STRINGSHARE_SMALL_BUCKET_STEP;
249 if (!_eina_stringshare_small_bucket_resize(bucket, size))
253 snew = malloc(length + 1);
254 if (!snew) return NULL;
256 memcpy(snew, str, length);
260 todo = bucket->count - idx;
263 memmove((void *)(bucket->strings + off), bucket->strings + idx,
264 todo * sizeof(bucket->strings[0]));
265 memmove(bucket->lengths + off, bucket->lengths + idx,
266 todo * sizeof(bucket->lengths[0]));
267 memmove(bucket->references + off, bucket->references + idx,
268 todo * sizeof(bucket->references[0]));
271 bucket->strings[idx] = snew;
272 bucket->lengths[idx] = length;
273 bucket->references[idx] = 1;
280 _eina_stringshare_small_bucket_remove_at(
281 Eina_Stringshare_Small_Bucket **p_bucket,
284 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
287 if (bucket->references[idx] > 1)
289 bucket->references[idx]--;
293 free((char *)bucket->strings[idx]);
295 if (bucket->count == 1)
297 free((void *)bucket->strings);
298 free(bucket->lengths);
299 free(bucket->references);
306 if (idx == bucket->count)
310 todo = bucket->count - idx;
312 memmove((void *)(bucket->strings + idx), bucket->strings + off,
313 todo * sizeof(bucket->strings[0]));
314 memmove(bucket->lengths + idx, bucket->lengths + off,
315 todo * sizeof(bucket->lengths[0]));
316 memmove(bucket->references + idx, bucket->references + off,
317 todo * sizeof(bucket->references[0]));
320 if (bucket->count + EINA_STRINGSHARE_SMALL_BUCKET_STEP < bucket->size)
322 int size = bucket->size - EINA_STRINGSHARE_SMALL_BUCKET_STEP;
323 _eina_stringshare_small_bucket_resize(bucket, size);
328 _eina_stringshare_small_add(const char *str, unsigned char length)
330 Eina_Stringshare_Small_Bucket **bucket;
333 bucket = _eina_small_share.buckets + (unsigned char)str[0];
339 ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
342 (*bucket)->references[i]++;
347 return _eina_stringshare_small_bucket_insert_at(bucket, str, length, i);
351 _eina_stringshare_small_del(const char *str, unsigned char length)
353 Eina_Stringshare_Small_Bucket **bucket;
357 bucket = _eina_small_share.buckets + (unsigned char)str[0];
361 ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
365 _eina_stringshare_small_bucket_remove_at(bucket, i);
369 CRI("EEEK trying to del non-shared stringshare \"%s\"", str);
373 _eina_stringshare_small_init(void)
375 eina_spinlock_new(&_mutex_small);
376 memset(&_eina_small_share, 0, sizeof(_eina_small_share));
380 _eina_stringshare_small_shutdown(void)
382 Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
384 p_bucket = _eina_small_share.buckets;
385 p_bucket_end = p_bucket + 256;
387 for (; p_bucket < p_bucket_end; p_bucket++)
389 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
395 s = (char **)bucket->strings;
396 s_end = s + bucket->count;
397 for (; s < s_end; s++)
400 free((void *)bucket->strings);
401 free(bucket->lengths);
402 free(bucket->references);
407 eina_spinlock_free(&_mutex_small);
411 _eina_stringshare_small_bucket_dump(Eina_Stringshare_Small_Bucket *bucket,
414 const char **s = bucket->strings;
415 unsigned char *l = bucket->lengths;
416 unsigned int *r = bucket->references;
419 di->used += sizeof(*bucket);
420 di->used += bucket->count * sizeof(*s);
421 di->used += bucket->count * sizeof(*l);
422 di->used += bucket->count * sizeof(*r);
423 di->unique += bucket->count;
425 for (i = 0; i < bucket->count; i++, s++, l++, r++)
429 printf("DDD: %5hhu %5u '%s'\n", *l, *r, *s);
434 di->saved += *l * dups;
440 _eina_stringshare_small_dump(struct dumpinfo *di)
442 Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
444 p_bucket = _eina_small_share.buckets;
445 p_bucket_end = p_bucket + 256;
447 for (; p_bucket < p_bucket_end; p_bucket++)
449 Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
454 _eina_stringshare_small_bucket_dump(bucket, di);
459 /*============================================================================*
461 *============================================================================*/
465 * @brief Initialize the share_common module.
467 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
469 * This function sets up the share_common module of Eina. It is called by
475 eina_stringshare_init(void)
479 if (_eina_share_stringshare_log_dom < 0)
481 _eina_share_stringshare_log_dom = eina_log_domain_register
482 ("eina_stringshare", EINA_LOG_COLOR_DEFAULT);
484 if (_eina_share_stringshare_log_dom < 0)
486 EINA_LOG_ERR("Could not register log domain: eina_stringshare");
491 ret = eina_share_common_init(&stringshare_share,
492 EINA_MAGIC_STRINGSHARE_NODE,
493 EINA_MAGIC_STRINGSHARE_NODE_STR);
495 _eina_stringshare_small_init();
498 eina_log_domain_unregister(_eina_share_stringshare_log_dom);
499 _eina_share_stringshare_log_dom = -1;
507 * @brief Shut down the share_common module.
509 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
511 * This function shuts down the share_common module set up by
512 * eina_share_common_init(). It is called by eina_shutdown().
514 * @see eina_shutdown()
517 eina_stringshare_shutdown(void)
520 _eina_stringshare_small_shutdown();
521 ret = eina_share_common_shutdown(&stringshare_share);
523 if (_eina_share_stringshare_log_dom >= 0)
525 eina_log_domain_unregister(_eina_share_stringshare_log_dom);
526 _eina_share_stringshare_log_dom = -1;
532 /*============================================================================*
534 *============================================================================*/
537 eina_stringshare_del(Eina_Stringshare *str)
547 else if (str[1] == '\0')
549 else if (str[2] == '\0')
551 else if (str[3] == '\0')
554 slen = 4; /* handled later */
558 eina_share_common_population_del(stringshare_share, slen);
564 eina_share_common_population_del(stringshare_share, slen);
565 eina_spinlock_take(&_mutex_small);
566 _eina_stringshare_small_del(str, slen);
567 eina_spinlock_release(&_mutex_small);
572 if (!eina_share_common_del(stringshare_share, str))
573 CRI("EEEK trying to del non-shared stringshare \"%s\"", str);
576 EAPI Eina_Stringshare *
577 eina_stringshare_add_length(const char *str, unsigned int slen)
583 eina_share_common_population_add(stringshare_share, slen);
589 eina_share_common_population_add(stringshare_share, slen);
591 return (Eina_Stringshare *) _eina_stringshare_single + ((*str) << 1);
597 eina_share_common_population_add(stringshare_share, slen);
598 eina_spinlock_take(&_mutex_small);
599 s = _eina_stringshare_small_add(str, slen);
600 eina_spinlock_release(&_mutex_small);
605 return eina_share_common_add_length(stringshare_share, str, slen *
606 sizeof(char), sizeof(char));
609 EAPI Eina_Stringshare *
610 eina_stringshare_add(const char *str)
612 if (!str) return NULL;
613 return eina_stringshare_add_length(str, strlen(str));
616 EAPI Eina_Stringshare *
617 eina_stringshare_printf(const char *fmt, ...)
621 const char *ret = "";
628 len = vasprintf(&tmp, fmt, args);
631 if (len < 1) goto on_error;
633 ret = eina_stringshare_add_length(tmp, len);
640 EAPI Eina_Stringshare *
641 eina_stringshare_vprintf(const char *fmt, va_list args)
644 const char *ret = "";
650 len = vasprintf(&tmp, fmt, args);
652 if (len < 1) goto on_error;
654 ret = eina_stringshare_add_length(tmp, len);
661 EAPI Eina_Stringshare *
662 eina_stringshare_nprintf(unsigned int len, const char *fmt, ...)
674 tmp = alloca(sizeof(char) * (len + 1));
677 size = vsnprintf(tmp, len, fmt, args);
683 return eina_stringshare_add_length(tmp, size);
686 EAPI Eina_Stringshare *
687 eina_stringshare_ref(Eina_Stringshare *str)
697 else if (str[1] == '\0')
699 else if (str[2] == '\0')
701 else if (str[3] == '\0')
704 slen = 3 + (int)strlen(str + 3);
708 eina_share_common_population_add(stringshare_share, slen);
716 eina_share_common_population_add(stringshare_share, slen);
717 eina_spinlock_take(&_mutex_small);
718 s = _eina_stringshare_small_add(str, slen);
719 eina_spinlock_release(&_mutex_small);
724 return eina_share_common_ref(stringshare_share, str);
728 eina_stringshare_strlen(Eina_Stringshare *str)
747 len = eina_share_common_length(stringshare_share, (Eina_Stringshare *) str);
748 len = (len > 0) ? len / (int)sizeof(char) : -1;
753 eina_stringshare_dump(void)
755 eina_share_common_dump(stringshare_share,
756 _eina_stringshare_small_dump,
757 sizeof(_eina_stringshare_single));