API BREAK: eina_magic_string_set() does not change existing strings anymore.
[profile/ivi/eina.git] / src / lib / eina_magic.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2008 Cedric Bail
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef HAVE_EVIL
27 # include <Evil.h>
28 #endif
29
30 #include "eina_config.h"
31 #include "eina_private.h"
32 #include "eina_error.h"
33 #include "eina_log.h"
34
35 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
36 #include "eina_safety_checks.h"
37 #include "eina_magic.h"
38
39 /*============================================================================*
40  *                                  Local                                     *
41  *============================================================================*/
42
43 /**
44  * @cond LOCAL
45  */
46
47 typedef struct _Eina_Magic_String Eina_Magic_String;
48 struct _Eina_Magic_String
49 {
50    Eina_Magic magic;
51    char *string;
52 };
53
54 static int _eina_magic_string_log_dom = -1;
55
56 #define ERR(...) EINA_LOG_DOM_ERR(_eina_magic_string_log_dom, __VA_ARGS__)
57 #define DBG(...) EINA_LOG_DOM_DBG(_eina_magic_string_log_dom, __VA_ARGS__)
58
59 static Eina_Magic_String *_eina_magic_strings = NULL;
60 static size_t _eina_magic_strings_count = 0;
61 static size_t _eina_magic_strings_allocated = 0;
62 static Eina_Bool _eina_magic_strings_dirty = 0;
63
64 static int
65 _eina_magic_strings_sort_cmp(const void *p1, const void *p2)
66 {
67    const Eina_Magic_String *a = p1, *b = p2;
68    return a->magic - b->magic;
69 }
70
71 static int
72 _eina_magic_strings_find_cmp(const void *p1, const void *p2)
73 {
74    Eina_Magic a = (long)p1;
75    const Eina_Magic_String *b = p2;
76    return a - b->magic;
77 }
78
79 /**
80  * @endcond
81  */
82
83 /*============================================================================*
84  *                                 Global                                     *
85  *============================================================================*/
86
87 /*============================================================================*
88  *                                   API                                      *
89  *============================================================================*/
90
91 /**
92  * @addtogroup Eina_Magic_Group Magic
93  *
94  * @brief These functions provide magic checks management for projects.
95  *
96  * @{
97  */
98
99 /**
100  * @internal
101  * @brief Initialize the magic string module.
102  *
103  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
104  *
105  * This function sets up the magic string module of Eina. It is called by
106  * eina_init().
107  *
108  * @see eina_init()
109  */
110 Eina_Bool
111 eina_magic_string_init(void)
112 {
113    _eina_magic_string_log_dom = eina_log_domain_register
114      ("eina_magic_string", EINA_LOG_COLOR_DEFAULT);
115    if (_eina_magic_string_log_dom < 0)
116      {
117         EINA_LOG_ERR("Could not register log domain: eina_magic_string");
118         return EINA_FALSE;
119      }
120
121    return EINA_TRUE;
122 }
123
124 /**
125  * @internal
126  * @brief Shut down the magic string module.
127  *
128  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
129  *
130  * This function shuts down the magic string module set up by
131  * eina_magic string_init(). It is called by eina_shutdown().
132  *
133  * @see eina_shutdown()
134  */
135 Eina_Bool
136 eina_magic_string_shutdown(void)
137 {
138    size_t i;
139
140    for (i = 0; i < _eina_magic_strings_count; i++)
141      free(_eina_magic_strings[i].string);
142
143    free(_eina_magic_strings);
144    _eina_magic_strings = NULL;
145    _eina_magic_strings_count = 0;
146    _eina_magic_strings_allocated = 0;
147
148    eina_log_domain_unregister(_eina_magic_string_log_dom);
149    _eina_magic_string_log_dom = -1;
150
151    return EINA_TRUE;
152 }
153
154 /**
155  * @brief Return the string associated to the given magic identifier.
156  *
157  * @param magic The magic identifier.
158  * @return The string associated to the identifier.
159  *
160  * This function returns the string associated to @p magic. If none
161  * are found, the this function returns @c NULL. The returned value
162  * must not be freed.
163  */
164 EAPI const char*
165 eina_magic_string_get(Eina_Magic magic)
166 {
167    Eina_Magic_String *ems;
168
169    if (!_eina_magic_strings)
170      return NULL;
171
172    if (_eina_magic_strings_dirty)
173      {
174         qsort(_eina_magic_strings, _eina_magic_strings_count,
175               sizeof(Eina_Magic_String), _eina_magic_strings_sort_cmp);
176         _eina_magic_strings_dirty = 0;
177      }
178
179    ems = bsearch((void *)(long)magic, _eina_magic_strings,
180                  _eina_magic_strings_count, sizeof(Eina_Magic_String),
181                  _eina_magic_strings_find_cmp);
182    if (ems)
183      return ems->string;
184    return NULL;
185 }
186
187 /**
188  * @brief Set the string associated to the given magic identifier.
189  *
190  * @param magic The magic identifier.
191  * @param The string associated to the identifier, must not be @c NULL.
192  *
193  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
194  *
195  * This function sets the string @p magic_name to @p magic. It is not
196  * checked if number or string are already set, then you might end
197  * with duplicates in that case.
198  */
199 EAPI Eina_Bool
200 eina_magic_string_set(Eina_Magic magic, const char *magic_name)
201 {
202    Eina_Magic_String *ems;
203
204    EINA_SAFETY_ON_NULL_RETURN_VAL(magic_name, EINA_FALSE);
205
206    if (_eina_magic_strings_count == _eina_magic_strings_allocated)
207      {
208         void *tmp;
209         size_t size;
210
211         if (EINA_UNLIKELY(_eina_magic_strings_allocated == 0))
212           size = 48;
213         else
214           size = _eina_magic_strings_allocated + 16;
215
216         tmp = realloc(_eina_magic_strings, sizeof(Eina_Magic_String) * size);
217         if (!tmp)
218           {
219              ERR("could not realloc magic_strings from %zu to %zu buckets.",
220                  _eina_magic_strings_allocated, size);
221              return EINA_FALSE;
222           }
223         _eina_magic_strings = tmp;
224         _eina_magic_strings_allocated = size;
225      }
226
227    ems = _eina_magic_strings + _eina_magic_strings_count;
228    ems->magic = magic;
229    ems->string = strdup(magic_name);
230    if (!ems->string)
231      {
232         ERR("could not allocate string '%s'", magic_name);
233         return EINA_FALSE;
234      }
235
236    _eina_magic_strings_count++;
237    _eina_magic_strings_dirty = 1;
238    return EINA_TRUE;
239 }
240
241 #ifdef eina_magic_fail
242 # undef eina_magic_fail
243 #endif
244
245 /**
246  * @brief Display a message or abort is a magic check failed.
247  *
248  * @param d The checked data pointer.
249  * @param m The magic identifer to check.
250  * @param req_m The requested magic identifier to check.
251  * @param file The file in which the magic check failed.
252  * @param fcn The function in which the magic check failed.
253  * @param line The line at which the magic check failed.
254  *
255  * This function displays an error message if a magic check has
256  * failed, using the following logic in the following order:
257  * @li If @p d is @c NULL, a message warns about a @c NULL pointer.
258  * @li Otherwise, if @p m is equal to #EINA_MAGIC_NONE, a message
259  * warns about a handle that was already freed.
260  * @li Otherwise, if @p m is equal to @p req_m, a message warns about
261  * a handle that is of wrong type.
262  * @li Otherwise, a message warns you about ab-using that function...
263  *
264  * If the environment variable EINA_ERROR_ABORT is set, abort() is
265  * called and the program stops. It is useful for debugging programs
266  * with gdb.
267  */
268 EAPI void
269 eina_magic_fail(void *d, Eina_Magic m, Eina_Magic req_m, const char *file, const char *fnc, int line)
270 {
271    if (!d)
272      eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
273                     file, fnc, line,
274                     "*** Eina Magic Check Failed !!!\n"
275                     "    Input handle pointer is NULL !\n"
276                     "*** NAUGHTY PROGRAMMER!!!\n"
277                     "*** SPANK SPANK SPANK!!!\n"
278                     "*** Now go fix your code. Tut tut tut!\n"
279                     "\n");
280    else
281      if (m == EINA_MAGIC_NONE)
282        eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
283                       file, fnc, line,
284                       "*** Eina Magic Check Failed !!!\n"
285                       "    Input handle has already been freed!\n"
286                       "*** NAUGHTY PROGRAMMER!!!\n"
287                       "*** SPANK SPANK SPANK!!!\n"
288                       "*** Now go fix your code. Tut tut tut!\n"
289                       "\n");
290      else
291        if (m != req_m)
292        eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
293                       file, fnc, line,
294                       "*** Eina Magic Check Failed !!!\n"
295                       "    Input handle is wrong type\n"
296                       "    Expected: %08x - %s\n"
297                       "    Supplied: %08x - %s\n"
298                       "*** NAUGHTY PROGRAMMER!!!\n"
299                       "*** SPANK SPANK SPANK!!!\n"
300                       "*** Now go fix your code. Tut tut tut!\n"
301                       "\n",
302                       req_m, eina_magic_string_get(req_m),
303                       m, eina_magic_string_get(m));
304        else
305        eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
306                       file, fnc, line,
307                       "*** Eina Magic Check Failed !!!\n"
308                       "    Why did you call me !\n"
309                       "*** NAUGHTY PROGRAMMER!!!\n"
310                       "*** SPANK SPANK SPANK!!!\n"
311                       "*** Now go fix your code. Tut tut tut!\n"
312                       "\n");
313 }
314
315 /**
316  * @}
317  */