Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-string-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28
29 #include "camel-string-utils.h"
30
31 gint
32 camel_strcase_equal (gconstpointer a,
33                      gconstpointer b)
34 {
35         return (g_ascii_strcasecmp ((const gchar *) a, (const gchar *) b) == 0);
36 }
37
38 guint
39 camel_strcase_hash (gconstpointer v)
40 {
41         const gchar *p = (gchar *) v;
42         guint h = 0, g;
43
44         for (; *p != '\0'; p++) {
45                 h = (h << 4) + g_ascii_toupper (*p);
46                 if ((g = h & 0xf0000000)) {
47                         h = h ^ (g >> 24);
48                         h = h ^ g;
49                 }
50         }
51
52         return h;
53 }
54
55 static void
56 free_string (gpointer string,
57              gpointer user_data)
58 {
59         g_free (string);
60 }
61
62 void
63 camel_string_list_free (GList *string_list)
64 {
65         if (string_list == NULL)
66                 return;
67
68         g_list_foreach (string_list, free_string, NULL);
69         g_list_free (string_list);
70 }
71
72 gchar *
73 camel_strstrcase (const gchar *haystack,
74                   const gchar *needle)
75 {
76         /* find the needle in the haystack neglecting case */
77         const gchar *ptr;
78         guint len;
79
80         g_return_val_if_fail (haystack != NULL, NULL);
81         g_return_val_if_fail (needle != NULL, NULL);
82
83         len = strlen (needle);
84         if (len > strlen (haystack))
85                 return NULL;
86
87         if (len == 0)
88                 return (gchar *) haystack;
89
90         for (ptr = haystack; *(ptr + len - 1) != '\0'; ptr++)
91                 if (!g_ascii_strncasecmp (ptr, needle, len))
92                         return (gchar *) ptr;
93
94         return NULL;
95 }
96
97 const gchar *
98 camel_strdown (gchar *str)
99 {
100         register gchar *s = str;
101
102         while (*s) {
103                 if (*s >= 'A' && *s <= 'Z')
104                         *s += 0x20;
105                 s++;
106         }
107
108         return str;
109 }
110
111 /**
112  * camel_tolower:
113  * @c:
114  *
115  * ASCII to-lower function.
116  *
117  * Returns:
118  **/
119 gchar camel_tolower (gchar c)
120 {
121         if (c >= 'A' && c <= 'Z')
122                 c |= 0x20;
123
124         return c;
125 }
126
127 /**
128  * camel_toupper:
129  * @c:
130  *
131  * ASCII to-upper function.
132  *
133  * Returns:
134  **/
135 gchar camel_toupper (gchar c)
136 {
137         if (c >= 'a' && c <= 'z')
138                 c &= ~0x20;
139
140         return c;
141 }
142
143 /* working stuff for pstrings */
144 static GMutex string_pool_lock;
145 static GHashTable *string_pool = NULL;
146
147 typedef struct _StringPoolNode StringPoolNode;
148
149 struct _StringPoolNode {
150         gchar *string;
151         gulong ref_count;
152 };
153
154 static StringPoolNode *
155 string_pool_node_new (gchar *string)
156 {
157         StringPoolNode *node;
158
159         node = g_slice_new (StringPoolNode);
160         node->string = string;  /* takes ownership */
161         node->ref_count = 1;
162
163         return node;
164 }
165
166 static void
167 string_pool_node_free (StringPoolNode *node)
168 {
169         g_free (node->string);
170
171         g_slice_free (StringPoolNode, node);
172 }
173
174 static guint
175 string_pool_node_hash (const StringPoolNode *node)
176 {
177         return g_str_hash (node->string);
178 }
179
180 static gboolean
181 string_pool_node_equal (const StringPoolNode *node_a,
182                         const StringPoolNode *node_b)
183 {
184         return g_str_equal (node_a->string, node_b->string);
185 }
186
187 static void
188 string_pool_init (void)
189 {
190         if (G_UNLIKELY (string_pool == NULL))
191                 string_pool = g_hash_table_new_full (
192                         (GHashFunc) string_pool_node_hash,
193                         (GEqualFunc) string_pool_node_equal,
194                         (GDestroyNotify) string_pool_node_free,
195                         (GDestroyNotify) NULL);
196 }
197
198 /**
199  * camel_pstring_add:
200  * @string: string to add to the string pool
201  * @own: whether the string pool will own the memory pointed to by
202  *       @string, if @string is not yet in the pool
203  *
204  * Add @string to the pool.
205  *
206  * The %NULL and empty strings are special cased to constant values.
207  *
208  * Unreference the returned string with camel_pstring_free().
209  *
210  * Returns: a canonicalized copy of @string
211  **/
212 const gchar *
213 camel_pstring_add (gchar *string,
214                    gboolean own)
215 {
216         StringPoolNode static_node = { string, };
217         StringPoolNode *node;
218         const gchar *interned;
219
220         if (string == NULL)
221                 return NULL;
222
223         if (*string == '\0') {
224                 if (own)
225                         g_free (string);
226                 return "";
227         }
228
229         g_mutex_lock (&string_pool_lock);
230
231         string_pool_init ();
232
233         node = g_hash_table_lookup (string_pool, &static_node);
234
235         if (node != NULL) {
236                 node->ref_count++;
237                 if (own)
238                         g_free (string);
239         } else {
240                 if (!own)
241                         string = g_strdup (string);
242                 node = string_pool_node_new (string);
243                 g_hash_table_add (string_pool, node);
244         }
245
246         interned = node->string;
247
248         g_mutex_unlock (&string_pool_lock);
249
250         return interned;
251 }
252
253 /**
254  * camel_pstring_peek:
255  * @string: string to fetch from the string pool
256  *
257  * Returns the canonicalized copy of @string without increasing its
258  * reference count in the string pool.  If necessary, @string is first
259  * added to the string pool.
260  *
261  * The %NULL and empty strings are special cased to constant values.
262  *
263  * Returns: a canonicalized copy of @string
264  *
265  * Since: 2.24
266  **/
267 const gchar *
268 camel_pstring_peek (const gchar *string)
269 {
270         StringPoolNode static_node = { (gchar *) string, };
271         StringPoolNode *node;
272         const gchar *interned;
273
274         if (string == NULL)
275                 return NULL;
276
277         if (*string == '\0')
278                 return "";
279
280         g_mutex_lock (&string_pool_lock);
281
282         string_pool_init ();
283
284         node = g_hash_table_lookup (string_pool, &static_node);
285
286         if (node == NULL) {
287                 node = string_pool_node_new (g_strdup (string));
288                 g_hash_table_add (string_pool, node);
289         }
290
291         interned = node->string;
292
293         g_mutex_unlock (&string_pool_lock);
294
295         return interned;
296 }
297
298 /**
299  * camel_pstring_strdup:
300  * @string: string to copy
301  *
302  * Create a new pooled string entry for @strings.  A pooled string
303  * is a table where common strings are canonicalized.  They are also
304  * reference counted and freed when no longer referenced.
305  *
306  * The %NULL and empty strings are special cased to constant values.
307  *
308  * Unreference the returned string with camel_pstring_free().
309  *
310  * Returns: a canonicalized copy of @string
311  **/
312 const gchar *
313 camel_pstring_strdup (const gchar *string)
314 {
315         return camel_pstring_add ((gchar *) string, FALSE);
316 }
317
318 /**
319  * camel_pstring_free:
320  * @string: string to free
321  *
322  * Unreferences a pooled string.  If the string's reference count drops to
323  * zero it will be deallocated.  %NULL and the empty string are special cased.
324  **/
325 void
326 camel_pstring_free (const gchar *string)
327 {
328         StringPoolNode static_node = { (gchar *) string, };
329         StringPoolNode *node;
330
331         if (string_pool == NULL)
332                 return;
333
334         if (string == NULL || *string == '\0')
335                 return;
336
337         g_mutex_lock (&string_pool_lock);
338
339         node = g_hash_table_lookup (string_pool, &static_node);
340
341         if (node == NULL) {
342                 g_warning ("%s: String not in pool: %s", G_STRFUNC, string);
343         } else if (node->string != string) {
344                 g_warning ("%s: String is not ours: %s", G_STRFUNC, string);
345         } else if (node->ref_count == 0) {
346                 g_warning ("%s: Orphaned pool node: %s", G_STRFUNC, string);
347         } else {
348                 node->ref_count--;
349                 if (node->ref_count == 0)
350                         g_hash_table_remove (string_pool, node);
351         }
352
353         g_mutex_unlock (&string_pool_lock);
354 }
355
356 /**
357  * camel_pstring_dump_stat:
358  *
359  * Dumps to stdout memory statistic about the string pool.
360  *
361  * Since: 3.6
362  **/
363 void
364 camel_pstring_dump_stat (void)
365 {
366         g_mutex_lock (&string_pool_lock);
367
368         g_print ("   String Pool Statistics: ");
369
370         if (string_pool == NULL) {
371                 g_print ("Not used yet\n");
372         } else {
373                 GHashTableIter iter;
374                 gchar *format_size;
375                 guint64 bytes = 0;
376                 gpointer key;
377
378                 g_hash_table_iter_init (&iter, string_pool);
379
380                 while (g_hash_table_iter_next (&iter, &key, NULL))
381                         bytes += strlen (((StringPoolNode *) key)->string);
382
383                 format_size = g_format_size_full (
384                         bytes, G_FORMAT_SIZE_LONG_FORMAT);
385
386                 g_print (
387                         "Holds %d strings totaling %s\n",
388                         g_hash_table_size (string_pool),
389                         format_size);
390
391                 g_free (format_size);
392         }
393
394         g_mutex_unlock (&string_pool_lock);
395 }