/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors: Jeffrey Stedfast
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "camel-string-utils.h"
gint
camel_strcase_equal (gconstpointer a,
gconstpointer b)
{
return (g_ascii_strcasecmp ((const gchar *) a, (const gchar *) b) == 0);
}
guint
camel_strcase_hash (gconstpointer v)
{
const gchar *p = (gchar *) v;
guint h = 0, g;
for (; *p != '\0'; p++) {
h = (h << 4) + g_ascii_toupper (*p);
if ((g = h & 0xf0000000)) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return h;
}
gchar *
camel_strstrcase (const gchar *haystack,
const gchar *needle)
{
/* find the needle in the haystack neglecting case */
const gchar *ptr;
guint len;
g_return_val_if_fail (haystack != NULL, NULL);
g_return_val_if_fail (needle != NULL, NULL);
len = strlen (needle);
if (len > strlen (haystack))
return NULL;
if (len == 0)
return (gchar *) haystack;
for (ptr = haystack; *(ptr + len - 1) != '\0'; ptr++)
if (!g_ascii_strncasecmp (ptr, needle, len))
return (gchar *) ptr;
return NULL;
}
const gchar *
camel_strdown (gchar *str)
{
register gchar *s = str;
while (*s) {
if (*s >= 'A' && *s <= 'Z')
*s += 0x20;
s++;
}
return str;
}
/* working stuff for pstrings */
static GMutex string_pool_lock;
static GHashTable *string_pool = NULL;
typedef struct _StringPoolNode StringPoolNode;
struct _StringPoolNode {
gchar *string;
gulong ref_count;
};
static StringPoolNode *
string_pool_node_new (gchar *string)
{
StringPoolNode *node;
node = g_slice_new (StringPoolNode);
node->string = string; /* takes ownership */
node->ref_count = 1;
return node;
}
static void
string_pool_node_free (StringPoolNode *node)
{
g_free (node->string);
g_slice_free (StringPoolNode, node);
}
static guint
string_pool_node_hash (const StringPoolNode *node)
{
return g_str_hash (node->string);
}
static gboolean
string_pool_node_equal (const StringPoolNode *node_a,
const StringPoolNode *node_b)
{
return g_str_equal (node_a->string, node_b->string);
}
static void
string_pool_init (void)
{
if (G_UNLIKELY (string_pool == NULL))
string_pool = g_hash_table_new_full (
(GHashFunc) string_pool_node_hash,
(GEqualFunc) string_pool_node_equal,
(GDestroyNotify) string_pool_node_free,
(GDestroyNotify) NULL);
}
/**
* camel_pstring_add:
* @string: string to add to the string pool
* @own: whether the string pool will own the memory pointed to by
* @string, if @string is not yet in the pool
*
* Add @string to the pool.
*
* The %NULL and empty strings are special cased to constant values.
*
* Unreference the returned string with camel_pstring_free().
*
* Returns: a canonicalized copy of @string
**/
const gchar *
camel_pstring_add (gchar *string,
gboolean own)
{
StringPoolNode static_node = { string, };
StringPoolNode *node;
const gchar *interned;
if (string == NULL)
return NULL;
if (*string == '\0') {
if (own)
g_free (string);
return "";
}
g_mutex_lock (&string_pool_lock);
string_pool_init ();
node = g_hash_table_lookup (string_pool, &static_node);
if (node != NULL) {
node->ref_count++;
if (own)
g_free (string);
} else {
if (!own)
string = g_strdup (string);
node = string_pool_node_new (string);
g_hash_table_add (string_pool, node);
}
interned = node->string;
g_mutex_unlock (&string_pool_lock);
return interned;
}
/**
* camel_pstring_peek:
* @string: string to fetch from the string pool
*
* Returns the canonicalized copy of @string without increasing its
* reference count in the string pool. If necessary, @string is first
* added to the string pool.
*
* The %NULL and empty strings are special cased to constant values.
*
* Returns: a canonicalized copy of @string
*
* Since: 2.24
**/
const gchar *
camel_pstring_peek (const gchar *string)
{
StringPoolNode static_node = { (gchar *) string, };
StringPoolNode *node;
const gchar *interned;
if (string == NULL)
return NULL;
if (*string == '\0')
return "";
g_mutex_lock (&string_pool_lock);
string_pool_init ();
node = g_hash_table_lookup (string_pool, &static_node);
if (node == NULL) {
node = string_pool_node_new (g_strdup (string));
g_hash_table_add (string_pool, node);
}
interned = node->string;
g_mutex_unlock (&string_pool_lock);
return interned;
}
/**
* camel_pstring_strdup:
* @string: string to copy
*
* Create a new pooled string entry for @strings. A pooled string
* is a table where common strings are canonicalized. They are also
* reference counted and freed when no longer referenced.
*
* The %NULL and empty strings are special cased to constant values.
*
* Unreference the returned string with camel_pstring_free().
*
* Returns: a canonicalized copy of @string
**/
const gchar *
camel_pstring_strdup (const gchar *string)
{
return camel_pstring_add ((gchar *) string, FALSE);
}
/**
* camel_pstring_free:
* @string: string to free
*
* Unreferences a pooled string. If the string's reference count drops to
* zero it will be deallocated. %NULL and the empty string are special cased.
**/
void
camel_pstring_free (const gchar *string)
{
StringPoolNode static_node = { (gchar *) string, };
StringPoolNode *node;
if (string_pool == NULL)
return;
if (string == NULL || *string == '\0')
return;
g_mutex_lock (&string_pool_lock);
node = g_hash_table_lookup (string_pool, &static_node);
if (node == NULL) {
g_warning ("%s: String not in pool: %s", G_STRFUNC, string);
} else if (node->string != string) {
g_warning ("%s: String is not ours: %s", G_STRFUNC, string);
} else if (node->ref_count == 0) {
g_warning ("%s: Orphaned pool node: %s", G_STRFUNC, string);
} else {
node->ref_count--;
if (node->ref_count == 0)
g_hash_table_remove (string_pool, node);
}
g_mutex_unlock (&string_pool_lock);
}
/**
* camel_pstring_dump_stat:
*
* Dumps to stdout memory statistic about the string pool.
*
* Since: 3.6
**/
void
camel_pstring_dump_stat (void)
{
g_mutex_lock (&string_pool_lock);
g_print (" String Pool Statistics: ");
if (string_pool == NULL) {
g_print ("Not used yet\n");
} else {
GHashTableIter iter;
gchar *format_size;
guint64 bytes = 0;
gpointer key;
g_hash_table_iter_init (&iter, string_pool);
while (g_hash_table_iter_next (&iter, &key, NULL))
bytes += strlen (((StringPoolNode *) key)->string);
format_size = g_format_size_full (
bytes, G_FORMAT_SIZE_LONG_FORMAT);
g_print (
"Holds %d strings totaling %s\n",
g_hash_table_size (string_pool),
format_size);
g_free (format_size);
}
g_mutex_unlock (&string_pool_lock);
}