EFL 1.7 svn doobies
[profile/ivi/eina.git] / src / lib / eina_stringshare.c
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
41 extern "C"
42 # endif
43 void *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
81 static int _eina_share_stringshare_log_dom = -1;
82
83 /* The actual share */
84 static Eina_Share *stringshare_share;
85 static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node";
86
87 extern Eina_Bool _share_common_threads_activated;
88 static Eina_Lock _mutex_small;
89
90 /* Stringshare optimizations */
91 static 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
122 typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small;
123 typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket;
124
125 struct _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
135 struct _Eina_Stringshare_Small
136 {
137    Eina_Stringshare_Small_Bucket *buckets[256];
138 };
139
140 #define EINA_STRINGSHARE_SMALL_BUCKET_STEP 8
141 static Eina_Stringshare_Small _eina_small_share;
142
143 static 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
183 static 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
224 static 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
261 static 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
319 static 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
359 end:
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
367 static 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
390 static 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
408 error:
409    CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str);
410 }
411
412 static 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
419 static 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
450 static 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
479 static 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  */
514 Eina_Bool
515 eina_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  */
556 Eina_Bool
557 eina_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
576 EAPI void
577 eina_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
611 EAPI Eina_Stringshare *
612 eina_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
632 EAPI Eina_Stringshare *
633 eina_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
653 EAPI Eina_Stringshare *
654 eina_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
677 EAPI Eina_Stringshare *
678 eina_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
698 EAPI Eina_Stringshare *
699 eina_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
723 EAPI Eina_Stringshare *
724 eina_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
764 EAPI int
765 eina_stringshare_strlen(Eina_Stringshare *str)
766 {
767    int len;
768
769    if (!str) return 0;
770
771    /* special cases */
772    if (str[0] == '\0')
773      return 0;
774
775    if (str[1] == '\0')
776      return 1;
777
778    if (str[2] == '\0')
779      return 2;
780
781    if (str[3] == '\0')
782      return 3;
783
784    len = eina_share_common_length(stringshare_share, (Eina_Stringshare *) str);
785    len = (len > 0) ? len / (int)sizeof(char) : -1;
786    return len;
787 }
788
789 EAPI void
790 eina_stringshare_dump(void)
791 {
792    eina_share_common_dump(stringshare_share,
793                           _eina_stringshare_small_dump,
794                           sizeof(_eina_stringshare_single));
795 }