* eet: reduce memory used by Eet dictionary.
[framework/uifw/eet.git] / src / lib / eet_dictionary.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif /* ifdef HAVE_CONFIG_H */
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <math.h>
8
9 #include <Eina.h>
10
11 #include "Eet.h"
12 #include "Eet_private.h"
13
14 Eet_Dictionary *
15 eet_dictionary_add(void)
16 {
17    Eet_Dictionary *new;
18
19    new = calloc(1, sizeof (Eet_Dictionary));
20    if (!new)
21       return NULL;
22
23    memset(new->hash, -1, sizeof (int) * 256);
24
25    return new;
26 } /* eet_dictionary_add */
27
28 void
29 eet_dictionary_free(Eet_Dictionary *ed)
30 {
31    if (ed)
32      {
33         int i;
34
35         for (i = 0; i < ed->count; ++i)
36           if (ed->all[i].allocated)
37             free(ed->all[i].u.str);
38
39         if (ed->all)
40           free(ed->all);
41
42         if (ed->converts) eina_hash_free(ed->converts);
43
44         free(ed);
45      }
46 } /* eet_dictionary_free */
47
48 static int
49 _eet_dictionary_lookup(Eet_Dictionary *ed,
50                        const char     *string,
51                        int             len,
52                        int             hash)
53 {
54    Eina_Bool found = EINA_FALSE;
55    int prev = -1;
56    int current;
57
58    current = ed->hash[hash];
59
60    while (current != -1)
61      {
62         if (ed->all[current].len == len)
63           {
64              if (ed->all[current].allocated)
65                {
66                   if (strcmp(ed->all[current].u.str, string) == 0)
67                     {
68                        found = EINA_TRUE;
69                        break;
70                     }
71                }
72              else if (ed->all[current].u.mmap
73                       && strcmp(ed->all[current].u.mmap, string) == 0)
74                  {
75                     found = EINA_TRUE;
76                     break;
77                  }
78           }
79
80         prev = current;
81         current = ed->all[current].next;
82      }
83
84    if (current == -1 && found)
85       return prev;
86
87    return current;
88 } /* _eet_dictionary_lookup */
89
90 int
91 eet_dictionary_string_add(Eet_Dictionary *ed,
92                           const char     *string)
93 {
94    Eet_String *current;
95    char *str;
96    int hash;
97    int idx;
98    int len;
99
100    if (!ed)
101       return -1;
102
103    hash = _eet_hash_gen(string, 8);
104    len = strlen(string) + 1;
105
106    idx = _eet_dictionary_lookup(ed, string, len, hash);
107
108    if (idx != -1)
109      {
110         if (ed->all[idx].allocated)
111           {
112              if (strcmp(ed->all[idx].u.str, string) == 0)
113                return idx;
114           }
115         else if (ed->all[idx].u.mmap && strcmp(ed->all[idx].u.mmap, string) == 0)
116           return idx;
117      }
118
119    if (ed->total == ed->count)
120      {
121         Eet_String *new;
122         int total;
123
124         total = ed->total + 8;
125
126         new = realloc(ed->all, sizeof (Eet_String) * total);
127         if (!new)
128            return -1;
129
130         ed->all = new;
131         ed->total = total;
132      }
133
134    str = strdup(string);
135    if (!str)
136       return -1;
137
138    current = ed->all + ed->count;
139
140    current->allocated = EINA_TRUE;
141
142    current->hash = hash;
143
144    current->u.str = str;
145    current->len = len;
146
147    if (idx == -1)
148      {
149         current->next = ed->hash[hash];
150         current->prev = -1;
151         ed->hash[hash] = ed->count;
152      }
153    else
154      {
155         current->next = idx;
156         current->prev = ed->all[idx].prev;
157
158         if (current->next != -1)
159            ed->all[current->next].prev = ed->count;
160
161         if (current->prev != -1)
162            ed->all[current->prev].next = ed->count;
163         else
164            ed->hash[hash] = ed->count;
165      }
166
167    return ed->count++;
168 } /* eet_dictionary_string_add */
169
170 int
171 eet_dictionary_string_get_size(const Eet_Dictionary *ed,
172                                int                   idx)
173 {
174    if (!ed)
175       return 0;
176
177    if (idx < 0)
178       return 0;
179
180    if (idx < ed->count)
181       return ed->all[idx].len;
182
183    return 0;
184 } /* eet_dictionary_string_get_size */
185
186 int
187 eet_dictionary_string_get_hash(const Eet_Dictionary *ed,
188                                int                   idx)
189 {
190    if (!ed)
191       return -1;
192
193    if (idx < 0)
194       return -1;
195
196    if (idx < ed->count)
197       return ed->all[idx].hash;
198
199    return -1;
200 } /* eet_dictionary_string_get_hash */
201
202 const char *
203 eet_dictionary_string_get_char(const Eet_Dictionary *ed,
204                                int                   idx)
205 {
206    if (!ed)
207       return NULL;
208
209    if (idx < 0)
210       return NULL;
211
212    if (idx < ed->count)
213      {
214 #ifdef _WIN32
215         /* Windows file system could change the mmaped file when replacing a file. So we need to copy all string in memory to avoid bugs. */
216         if (!ed->all[idx].allocated)
217           {
218              ed->all[idx].u.str = strdup(ed->all[idx].u.mmap);
219              ed->all[idx].allocated = EINA_TRUE;
220           }
221
222 #else /* ifdef _WIN32 */
223         if (!(ed->all[idx].allocated) && ed->all[idx].u.mmap)
224            return ed->all[idx].u.mmap;
225
226 #endif /* ifdef _WIN32 */
227         return ed->all[idx].u.str;
228      }
229
230    return NULL;
231 } /* eet_dictionary_string_get_char */
232
233 static inline Eina_Bool
234 _eet_dictionary_string_get_me_cache(const char *s,
235                                     int         len,
236                                     int        *mantisse,
237                                     int        *exponent)
238 {
239    if ((len == 6) && (s[0] == '0') && (s[1] == 'x') && (s[3] == 'p'))
240      {
241         *mantisse = (s[2] >= 'a') ? (s[2] - 'a' + 10) : (s[2] - '0');
242         *exponent = (s[5] - '0');
243
244         return EINA_TRUE;
245      }
246
247    return EINA_FALSE;
248 } /* _eet_dictionary_string_get_me_cache */
249
250 static inline Eina_Bool
251 _eet_dictionary_string_get_float_cache(const char *s,
252                                        int         len,
253                                        float      *result)
254 {
255    int mantisse;
256    int exponent;
257
258    if (_eet_dictionary_string_get_me_cache(s, len, &mantisse, &exponent))
259      {
260         if (s[4] == '+')
261            *result = (float)(mantisse << exponent);
262         else
263            *result = (float)mantisse / (float)(1 << exponent);
264
265         return EINA_TRUE;
266      }
267
268    return EINA_FALSE;
269 } /* _eet_dictionary_string_get_float_cache */
270
271 static inline Eina_Bool
272 _eet_dictionary_string_get_double_cache(const char *s,
273                                         int         len,
274                                         double     *result)
275 {
276    int mantisse;
277    int exponent;
278
279    if (_eet_dictionary_string_get_me_cache(s, len, &mantisse, &exponent))
280      {
281         if (s[4] == '+')
282            *result = (double)(mantisse << exponent);
283         else
284            *result = (double)mantisse / (float)(1 << exponent);
285
286         return EINA_TRUE;
287      }
288
289    return EINA_FALSE;
290 } /* _eet_dictionary_string_get_double_cache */
291
292 static inline Eina_Bool
293 _eet_dictionary_test(const Eet_Dictionary *ed,
294                      int                   idx,
295                      void                 *result)
296 {
297    if (!result)
298       return EINA_FALSE;
299
300    if (!ed)
301       return EINA_FALSE;
302
303    if (idx < 0)
304       return EINA_FALSE;
305
306    if (!(idx < ed->count))
307       return EINA_FALSE;
308
309    return EINA_TRUE;
310 } /* _eet_dictionary_test */
311
312 static Eet_Convert *
313 eet_dictionary_convert_get(const Eet_Dictionary *ed,
314                            int idx,
315                            const char **str)
316 {
317    Eet_Convert *result;
318
319    *str = ed->all[idx].allocated ? ed->all[idx].u.str : ed->all[idx].u.mmap;
320
321    if (!ed->converts)
322      {
323         ((Eet_Dictionary*)ed)->converts = eina_hash_int32_new(free);
324
325         goto add_convert;
326      }
327
328    result = eina_hash_find(ed->converts, &idx);
329    if (result) return result;
330
331  add_convert:
332    result = calloc(1, sizeof (Eet_Convert));
333
334    eina_hash_add(ed->converts, &idx, result);
335    return result;
336 }
337
338 Eina_Bool
339 eet_dictionary_string_get_float(const Eet_Dictionary *ed,
340                                 int                   idx,
341                                 float                *result)
342 {
343    Eet_Convert *convert;
344    const char *str;
345
346    if (!_eet_dictionary_test(ed, idx, result))
347       return EINA_FALSE;
348
349    convert = eet_dictionary_convert_get(ed, idx, &str);
350    if (!convert) return EINA_FALSE;
351
352    if (!(convert->type & EET_D_FLOAT))
353      {
354         if (!_eet_dictionary_string_get_float_cache(str, ed->all[idx].len,
355                                                     &convert->f))
356           {
357              long long mantisse = 0;
358              long exponent = 0;
359
360              if (eina_convert_atod(str, ed->all[idx].len, &mantisse,
361                                    &exponent) == EINA_FALSE)
362                 return EINA_FALSE;
363
364              convert->f = ldexpf((float)mantisse, exponent);
365           }
366
367         convert->type |= EET_D_FLOAT;
368      }
369
370    *result = convert->f;
371    return EINA_TRUE;
372 } /* eet_dictionary_string_get_float */
373
374 Eina_Bool
375 eet_dictionary_string_get_double(const Eet_Dictionary *ed,
376                                  int                   idx,
377                                  double               *result)
378 {
379    Eet_Convert *convert;
380    const char *str;
381
382    if (!_eet_dictionary_test(ed, idx, result))
383       return EINA_FALSE;
384
385    convert = eet_dictionary_convert_get(ed, idx, &str);
386    if (!convert) return EINA_FALSE;
387
388    if (!(convert->type & EET_D_DOUBLE))
389      {
390         if (!_eet_dictionary_string_get_double_cache(str, ed->all[idx].len,
391                                                      &convert->d))
392           {
393              long long mantisse = 0;
394              long exponent = 0;
395
396              if (eina_convert_atod(str, ed->all[idx].len, &mantisse,
397                                    &exponent) == EINA_FALSE)
398                 return EINA_FALSE;
399
400              convert->d = ldexp((double)mantisse, exponent);
401           }
402
403         convert->type |= EET_D_DOUBLE;
404      }
405
406    *result = convert->d;
407    return EINA_TRUE;
408 } /* eet_dictionary_string_get_double */
409
410 Eina_Bool
411 eet_dictionary_string_get_fp(const Eet_Dictionary *ed,
412                              int                   idx,
413                              Eina_F32p32          *result)
414 {
415    Eet_Convert *convert;
416    const char *str;
417
418    if (!_eet_dictionary_test(ed, idx, result))
419       return EINA_FALSE;
420
421    convert = eet_dictionary_convert_get(ed, idx, &str);
422    if (!convert) return EINA_FALSE;
423
424    if (!(convert->type & EET_D_FIXED_POINT))
425      {
426         Eina_F32p32 fp;
427
428         if (!eina_convert_atofp(str, ed->all[idx].len, &fp))
429            return EINA_FALSE;
430
431         convert->fp = fp;
432         convert->type |= EET_D_FIXED_POINT;
433      }
434
435    *result = convert->fp;
436    return EINA_TRUE;
437 } /* eet_dictionary_string_get_fp */
438
439 EAPI int
440 eet_dictionary_string_check(Eet_Dictionary *ed,
441                             const char     *string)
442 {
443    int i;
444
445    if ((!ed) || (!string))
446       return 0;
447
448    if ((ed->start <= string) && (string < ed->end))
449       return 1;
450
451    for (i = 0; i < ed->count; ++i)
452      if ((ed->all[i].allocated) && ed->all[i].u.str == string)
453        return 1;
454
455    return 0;
456 } /* eet_dictionary_string_check */
457