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