unicorn/qemu/glib_compat.c

1941 lines
48 KiB
C
Raw Normal View History

/*
glib_compat.c replacement functionality for glib code used in qemu
Copyright (C) 2016 Chris Eagle cseagle at gmail dot com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
2016-12-27 02:15:08 +00:00
// Part of this code was lifted from glib-2.28.0.
// Glib license is available in COPYING_GLIB file in root directory.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "glib_compat.h"
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
2017-01-01 17:00:11 +00:00
#ifndef _WIN64
2017-04-12 06:01:58 +00:00
#define GPOINTER_TO_UINT(p) ((guint)(uintptr_t)(p))
2017-01-01 17:00:11 +00:00
#else
#define GPOINTER_TO_UINT(p) ((guint) (guint64) (p))
#endif
#define G_MAXINT INT_MAX
/* All functions below added to eliminate GLIB dependency */
/* hashing and equality functions */
// Hash functions lifted glib-2.28.0/glib/ghash.c
/**
* g_direct_hash:
* @v: a #gpointer key
*
* Converts a gpointer to a hash value.
* It can be passed to g_hash_table_new() as the @hash_func parameter,
* when using pointers as keys in a #GHashTable.
*
* Returns: a hash value corresponding to the key.
*/
static guint g_direct_hash (gconstpointer v)
{
return GPOINTER_TO_UINT (v);
}
// g_str_hash() is lifted glib-2.28.0/glib/gstring.c
/**
* g_str_hash:
* @v: a string key
*
* Converts a string to a hash value.
*
* This function implements the widely used "djb" hash apparently posted
* by Daniel Bernstein to comp.lang.c some time ago. The 32 bit
* unsigned hash value starts at 5381 and for each byte 'c' in the
* string, is updated: <literal>hash = hash * 33 + c</literal>. This
* function uses the signed value of each byte.
*
* It can be passed to g_hash_table_new() as the @hash_func parameter,
* when using strings as keys in a #GHashTable.
*
* Returns: a hash value corresponding to the key
**/
guint g_str_hash (gconstpointer v)
2016-12-19 14:18:33 +00:00
{
const signed char *p;
guint32 h = 5381;
for (p = v; *p != '\0'; p++)
h = (h << 5) + h + *p;
return h;
}
gboolean g_str_equal(gconstpointer v1, gconstpointer v2)
2016-12-19 14:18:33 +00:00
{
return strcmp((const char*)v1, (const char*)v2) == 0;
}
// g_int_hash() is lifted from glib-2.28.0/glib/gutils.c
/**
* g_int_hash:
* @v: a pointer to a #gint key
*
* Converts a pointer to a #gint to a hash value.
* It can be passed to g_hash_table_new() as the @hash_func parameter,
* when using pointers to integers values as keys in a #GHashTable.
*
* Returns: a hash value corresponding to the key.
*/
guint g_int_hash (gconstpointer v)
2016-12-19 14:18:33 +00:00
{
return *(const gint*) v;
}
gboolean g_int_equal(gconstpointer v1, gconstpointer v2)
2016-12-19 14:18:33 +00:00
{
return *((const gint*)v1) == *((const gint*)v2);
}
/* Doubly-linked list */
2016-12-19 14:18:33 +00:00
GList *g_list_first(GList *list)
{
if (list == NULL) return NULL;
while (list->prev) list = list->prev;
return list;
}
void g_list_foreach(GList *list, GFunc func, gpointer user_data)
2016-12-19 14:18:33 +00:00
{
GList *lp;
for (lp = list; lp; lp = lp->next) {
(*func)(lp->data, user_data);
}
}
2016-12-19 14:18:33 +00:00
void g_list_free(GList *list)
{
GList *lp, *next, *prev = NULL;
if (list) prev = list->prev;
for (lp = list; lp; lp = next) {
next = lp->next;
free(lp);
}
for (lp = prev; lp; lp = prev) {
prev = lp->prev;
free(lp);
}
}
GList *g_list_insert_sorted(GList *list, gpointer data, GCompareFunc compare)
2016-12-19 14:18:33 +00:00
{
GList *i;
GList *n = (GList*)g_malloc(sizeof(GList));
n->data = data;
if (list == NULL) {
n->next = n->prev = NULL;
return n;
}
for (i = list; i; i = i->next) {
n->prev = i->prev;
if ((*compare)(data, i->data) <= 0) {
n->next = i;
i->prev = n;
if (i == list) return n;
else return list;
}
}
n->prev = n->prev->next;
n->next = NULL;
n->prev->next = n;
return list;
}
GList *g_list_prepend(GList *list, gpointer data)
2016-12-19 14:18:33 +00:00
{
GList *n = (GList*)g_malloc(sizeof(GList));
n->next = list;
n->prev = NULL;
n->data = data;
return n;
}
2016-12-19 14:18:33 +00:00
GList *g_list_remove_link(GList *list, GList *llink)
{
2016-12-26 10:13:46 +00:00
if (llink) {
if (llink == list) list = list->next;
if (llink->prev) llink->prev->next = llink->next;
if (llink->next) llink->next->prev = llink->prev;
}
return list;
}
// code copied from glib/glist.c, version 2.28.0
static GList *g_list_sort_merge(GList *l1,
GList *l2,
GFunc compare_func,
gpointer user_data)
2016-12-19 14:18:33 +00:00
{
GList list, *l, *lprev;
gint cmp;
l = &list;
lprev = NULL;
while (l1 && l2)
{
cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
if (cmp <= 0)
{
l->next = l1;
l1 = l1->next;
}
else
{
l->next = l2;
l2 = l2->next;
}
l = l->next;
l->prev = lprev;
lprev = l;
}
l->next = l1 ? l1 : l2;
l->next->prev = l;
return list.next;
}
static GList *g_list_sort_real(GList *list,
GFunc compare_func,
gpointer user_data)
{
GList *l1, *l2;
if (!list)
return NULL;
if (!list->next)
return list;
l1 = list;
l2 = list->next;
while ((l2 = l2->next) != NULL)
{
if ((l2 = l2->next) == NULL)
break;
l1 = l1->next;
}
l2 = l1->next;
l1->next = NULL;
return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data),
g_list_sort_real (l2, compare_func, user_data),
compare_func,
user_data);
}
/**
* g_list_sort:
* @list: a #GList
* @compare_func: the comparison function used to sort the #GList.
* This function is passed the data from 2 elements of the #GList
* and should return 0 if they are equal, a negative value if the
* first element comes before the second, or a positive value if
* the first element comes after the second.
*
* Sorts a #GList using the given comparison function.
*
* Returns: the start of the sorted #GList
*/
/**
* GCompareFunc:
* @a: a value.
* @b: a value to compare with.
* @Returns: negative value if @a &lt; @b; zero if @a = @b; positive
* value if @a > @b.
*
* Specifies the type of a comparison function used to compare two
* values. The function should return a negative integer if the first
* value comes before the second, 0 if they are equal, or a positive
* integer if the first value comes after the second.
**/
GList *g_list_sort (GList *list, GCompareFunc compare_func)
{
return g_list_sort_real (list, (GFunc) compare_func, NULL);
}
static inline GList*
_g_list_remove_link (GList *list,
GList *link)
{
if (link)
{
if (link->prev)
link->prev->next = link->next;
if (link->next)
link->next->prev = link->prev;
if (link == list)
list = list->next;
link->next = NULL;
link->prev = NULL;
}
return list;
}
/**
* g_list_delete_link:
* @list: a #GList, this must point to the top of the list
* @link_: node to delete from @list
*
* Removes the node link_ from the list and frees it.
* Compare this to g_list_remove_link() which removes the node
* without freeing it.
*
* Returns: the (possibly changed) start of the #GList
*/
GList *
g_list_delete_link (GList *list,
GList *link_)
{
list = _g_list_remove_link (list, link_);
//_g_list_free1 (link_);
g_free (link_);
return list;
}
/**
* g_list_insert_before:
* @list: a pointer to a #GList
* @sibling: the list element before which the new element
* is inserted or %NULL to insert at the end of the list
* @data: the data for the new element
*
* Inserts a new element into the list before the given position.
*
* Returns: the new start of the #GList
*/
GList*
g_list_insert_before (GList *list,
GList *sibling,
gpointer data)
{
if (!list)
{
list = g_malloc(sizeof(GList));
list->data = data;
return list;
}
else if (sibling)
{
GList *node;
node = g_malloc(sizeof(GList));
node->data = data;
node->prev = sibling->prev;
node->next = sibling;
sibling->prev = node;
if (node->prev)
{
node->prev->next = node;
return list;
}
else
{
return node;
}
}
else
{
GList *last;
last = list;
while (last->next)
last = last->next;
last->next = g_malloc(sizeof(GList));
last->next->data = data;
last->next->prev = last;
last->next->next = NULL;
return list;
}
}
/* END of g_list related functions */
/* Singly-linked list */
GSList *g_slist_append(GSList *list, gpointer data)
2016-12-19 14:18:33 +00:00
{
GSList *head = list;
if (list) {
while (list->next) list = list->next;
list->next = (GSList*)g_malloc(sizeof(GSList));
list = list->next;
2016-12-26 10:13:46 +00:00
} else {
head = list = (GSList*)g_malloc(sizeof(GSList));
}
list->data = data;
list->next = NULL;
return head;
}
void g_slist_foreach(GSList *list, GFunc func, gpointer user_data)
2016-12-19 14:18:33 +00:00
{
GSList *lp;
for (lp = list; lp; lp = lp->next) {
(*func)(lp->data, user_data);
}
}
2016-12-19 14:18:33 +00:00
void g_slist_free(GSList *list)
{
GSList *lp, *next;
for (lp = list; lp; lp = next) {
next = lp->next;
free(lp);
}
}
GSList *g_slist_prepend(GSList *list, gpointer data)
2016-12-19 14:18:33 +00:00
{
GSList *head = (GSList*)g_malloc(sizeof(GSList));
head->next = list;
head->data = data;
return head;
}
static GSList *g_slist_sort_merge (GSList *l1,
GSList *l2,
GFunc compare_func,
gpointer user_data)
2016-12-19 14:18:33 +00:00
{
GSList list, *l;
gint cmp;
l=&list;
while (l1 && l2)
{
cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
if (cmp <= 0)
{
l=l->next=l1;
l1=l1->next;
}
else
{
l=l->next=l2;
l2=l2->next;
}
}
l->next= l1 ? l1 : l2;
return list.next;
}
static GSList *g_slist_sort_real (GSList *list,
GFunc compare_func,
gpointer user_data)
{
GSList *l1, *l2;
if (!list)
return NULL;
if (!list->next)
return list;
l1 = list;
l2 = list->next;
while ((l2 = l2->next) != NULL)
{
if ((l2 = l2->next) == NULL)
break;
l1=l1->next;
}
l2 = l1->next;
l1->next = NULL;
return g_slist_sort_merge (g_slist_sort_real (list, compare_func, user_data),
g_slist_sort_real (l2, compare_func, user_data),
compare_func,
user_data);
}
/**
* g_slist_sort:
* @list: a #GSList
* @compare_func: the comparison function used to sort the #GSList.
* This function is passed the data from 2 elements of the #GSList
* and should return 0 if they are equal, a negative value if the
* first element comes before the second, or a positive value if
* the first element comes after the second.
*
* Sorts a #GSList using the given comparison function.
*
* Returns: the start of the sorted #GSList
*/
GSList *g_slist_sort (GSList *list,
GCompareFunc compare_func)
{
return g_slist_sort_real (list, (GFunc) compare_func, NULL);
}
/* END of g_slist related functions */
// Hash functions lifted glib-2.28.0/glib/ghash.c
#define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */
typedef struct _GHashNode GHashNode;
struct _GHashNode {
gpointer key;
gpointer value;
/* If key_hash == 0, node is not in use
* If key_hash == 1, node is a tombstone
* If key_hash >= 2, node contains data */
guint key_hash;
};
2016-12-19 16:19:13 +00:00
struct _GHashTable {
gint size;
gint mod;
guint mask;
gint nnodes;
gint noccupied; /* nnodes + tombstones */
GHashNode *nodes;
GHashFunc hash_func;
GEqualFunc key_equal_func;
volatile gint ref_count;
GDestroyNotify key_destroy_func;
GDestroyNotify value_destroy_func;
2016-12-19 16:19:13 +00:00
};
/**
* g_hash_table_destroy:
* @hash_table: a #GHashTable.
*
* Destroys all keys and values in the #GHashTable and decrements its
* reference count by 1. If keys and/or values are dynamically allocated,
* you should either free them first or create the #GHashTable with destroy
* notifiers using g_hash_table_new_full(). In the latter case the destroy
* functions you supplied will be called on all keys and values during the
* destruction phase.
**/
void g_hash_table_destroy (GHashTable *hash_table)
2016-12-19 14:18:33 +00:00
{
if (hash_table == NULL) return;
if (hash_table->ref_count == 0) return;
g_hash_table_remove_all (hash_table);
g_hash_table_unref (hash_table);
}
/**
* g_hash_table_find:
* @hash_table: a #GHashTable.
* @predicate: function to test the key/value pairs for a certain property.
* @user_data: user data to pass to the function.
*
* Calls the given function for key/value pairs in the #GHashTable until
* @predicate returns %TRUE. The function is passed the key and value of
* each pair, and the given @user_data parameter. The hash table may not
* be modified while iterating over it (you can't add/remove items).
*
* Note, that hash tables are really only optimized for forward lookups,
* i.e. g_hash_table_lookup().
* So code that frequently issues g_hash_table_find() or
* g_hash_table_foreach() (e.g. in the order of once per every entry in a
* hash table) should probably be reworked to use additional or different
* data structures for reverse lookups (keep in mind that an O(n) find/foreach
* operation issued for all n values in a hash table ends up needing O(n*n)
* operations).
*
* Return value: The value of the first key/value pair is returned, for which
* func evaluates to %TRUE. If no pair with the requested property is found,
* %NULL is returned.
*
* Since: 2.4
**/
gpointer g_hash_table_find (GHashTable *hash_table,
GHRFunc predicate,
gpointer user_data)
2016-12-19 14:18:33 +00:00
{
gint i;
if (hash_table == NULL) return NULL;
if (predicate == NULL) return NULL;
for (i = 0; i < hash_table->size; i++)
{
GHashNode *node = &hash_table->nodes [i];
if (node->key_hash > 1 && predicate (node->key, node->value, user_data))
return node->value;
}
return NULL;
}
/**
* g_hash_table_foreach:
* @hash_table: a #GHashTable.
* @func: the function to call for each key/value pair.
* @user_data: user data to pass to the function.
*
* Calls the given function for each of the key/value pairs in the
* #GHashTable. The function is passed the key and value of each
* pair, and the given @user_data parameter. The hash table may not
* be modified while iterating over it (you can't add/remove
* items). To remove all items matching a predicate, use
* g_hash_table_foreach_remove().
*
* See g_hash_table_find() for performance caveats for linear
* order searches in contrast to g_hash_table_lookup().
**/
2016-12-26 16:13:27 +00:00
void g_hash_table_foreach (GHashTable *hash_table,
GHFunc func,
gpointer user_data)
2016-12-19 14:18:33 +00:00
{
gint i;
if (hash_table == NULL) return;
if (func == NULL) return;
for (i = 0; i < hash_table->size; i++)
{
GHashNode *node = &hash_table->nodes [i];
if (node->key_hash > 1)
(* func) (node->key, node->value, user_data);
}
}
/*
* g_hash_table_lookup_node_for_insertion:
* @hash_table: our #GHashTable
* @key: the key to lookup against
* @hash_return: key hash return location
* Return value: index of the described #GHashNode
*
* Performs a lookup in the hash table, preserving extra information
* usually needed for insertion.
*
* This function first computes the hash value of the key using the
* user's hash function.
*
* If an entry in the table matching @key is found then this function
* returns the index of that entry in the table, and if not, the
* index of an unused node (empty or tombstone) where the key can be
* inserted.
*
* The computed hash value is returned in the variable pointed to
* by @hash_return. This is to save insertions from having to compute
* the hash record again for the new record.
*/
static inline guint g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
gconstpointer key,
guint *hash_return)
2016-12-19 14:18:33 +00:00
{
GHashNode *node;
guint node_index;
guint hash_value;
guint first_tombstone;
gboolean have_tombstone = FALSE;
guint step = 0;
/* Empty buckets have hash_value set to 0, and for tombstones, it's 1.
* We need to make sure our hash value is not one of these. */
hash_value = (* hash_table->hash_func) (key);
if (hash_value <= 1)
hash_value = 2;
*hash_return = hash_value;
node_index = hash_value % hash_table->mod;
node = &hash_table->nodes [node_index];
while (node->key_hash)
{
/* We first check if our full hash values
* are equal so we can avoid calling the full-blown
* key equality function in most cases.
*/
if (node->key_hash == hash_value)
{
if (hash_table->key_equal_func)
{
if (hash_table->key_equal_func (node->key, key))
return node_index;
}
else if (node->key == key)
{
return node_index;
}
}
else if (node->key_hash == 1 && !have_tombstone)
{
first_tombstone = node_index;
have_tombstone = TRUE;
}
step++;
node_index += step;
node_index &= hash_table->mask;
node = &hash_table->nodes [node_index];
}
if (have_tombstone)
return first_tombstone;
return node_index;
}
/* Each table size has an associated prime modulo (the first prime
* lower than the table size) used to find the initial bucket. Probing
* then works modulo 2^n. The prime modulo is necessary to get a
* good distribution with poor hash functions. */
static const gint prime_mod [] = {
1, /* For 1 << 0 */
2,
3,
7,
13,
31,
61,
127,
251,
509,
1021,
2039,
4093,
8191,
16381,
32749,
65521, /* For 1 << 16 */
131071,
262139,
524287,
1048573,
2097143,
4194301,
8388593,
16777213,
33554393,
67108859,
134217689,
268435399,
536870909,
1073741789,
2147483647 /* For 1 << 31 */
};
static void g_hash_table_set_shift (GHashTable *hash_table, gint shift)
2016-12-19 14:18:33 +00:00
{
gint i;
guint mask = 0;
hash_table->size = 1 << shift;
hash_table->mod = prime_mod [shift];
for (i = 0; i < shift; i++)
{
mask <<= 1;
mask |= 1;
}
hash_table->mask = mask;
}
static gint g_hash_table_find_closest_shift (gint n)
{
gint i;
for (i = 0; n; i++)
n >>= 1;
return i;
}
static void g_hash_table_set_shift_from_size (GHashTable *hash_table, gint size)
{
gint shift;
shift = g_hash_table_find_closest_shift (size);
shift = MAX (shift, HASH_TABLE_MIN_SHIFT);
g_hash_table_set_shift (hash_table, shift);
}
/*
* g_hash_table_resize:
* @hash_table: our #GHashTable
*
* Resizes the hash table to the optimal size based on the number of
* nodes currently held. If you call this function then a resize will
* occur, even if one does not need to occur. Use
* g_hash_table_maybe_resize() instead.
*
* This function may "resize" the hash table to its current size, with
* the side effect of cleaning up tombstones and otherwise optimizing
* the probe sequences.
*/
static void g_hash_table_resize (GHashTable *hash_table)
{
GHashNode *new_nodes;
gint old_size;
gint i;
old_size = hash_table->size;
g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2);
new_nodes = g_new0 (GHashNode, hash_table->size);
for (i = 0; i < old_size; i++)
{
GHashNode *node = &hash_table->nodes [i];
GHashNode *new_node;
guint hash_val;
guint step = 0;
if (node->key_hash <= 1)
continue;
hash_val = node->key_hash % hash_table->mod;
new_node = &new_nodes [hash_val];
while (new_node->key_hash)
{
step++;
hash_val += step;
hash_val &= hash_table->mask; new_node = &new_nodes [hash_val];
}
*new_node = *node;
}
g_free (hash_table->nodes);
hash_table->nodes = new_nodes;
hash_table->noccupied = hash_table->nnodes;
}
/*
* g_hash_table_maybe_resize:
* @hash_table: our #GHashTable
*
* Resizes the hash table, if needed.
*
* Essentially, calls g_hash_table_resize() if the table has strayed
* too far from its ideal size for its number of nodes.
*/
static inline void g_hash_table_maybe_resize (GHashTable *hash_table)
{
gint noccupied = hash_table->noccupied;
gint size = hash_table->size;
if ((size > hash_table->nnodes * 4 && size > 1 << HASH_TABLE_MIN_SHIFT) ||
(size <= noccupied + (noccupied / 16)))
g_hash_table_resize (hash_table);
}
/*
* g_hash_table_insert_internal:
* @hash_table: our #GHashTable
* @key: the key to insert
* @value: the value to insert
* @keep_new_key: if %TRUE and this key already exists in the table
* then call the destroy notify function on the old key. If %FALSE
* then call the destroy notify function on the new key.
*
* Implements the common logic for the g_hash_table_insert() and
* g_hash_table_replace() functions.
*
* Do a lookup of @key. If it is found, replace it with the new
* @value (and perhaps the new @key). If it is not found, create a
* new node.
*/
static void g_hash_table_insert_internal (GHashTable *hash_table,
gpointer key,
gpointer value,
gboolean keep_new_key)
{
GHashNode *node;
guint node_index;
guint key_hash;
guint old_hash;
if (hash_table == NULL) return;
if (hash_table->ref_count == 0) return;
node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash);
node = &hash_table->nodes [node_index];
old_hash = node->key_hash;
if (old_hash > 1)
{
if (keep_new_key)
{
if (hash_table->key_destroy_func)
hash_table->key_destroy_func (node->key);
node->key = key;
}
else
{
if (hash_table->key_destroy_func)
hash_table->key_destroy_func (key);
}
if (hash_table->value_destroy_func)
hash_table->value_destroy_func (node->value);
node->value = value;
}
else
{
node->key = key;
node->value = value;
node->key_hash = key_hash;
hash_table->nnodes++;
if (old_hash == 0)
{
/* We replaced an empty node, and not a tombstone */
hash_table->noccupied++;
g_hash_table_maybe_resize (hash_table);
}
}
}
/**
* g_hash_table_insert:
* @hash_table: a #GHashTable.
* @key: a key to insert.
* @value: the value to associate with the key.
*
* Inserts a new key and value into a #GHashTable.
*
* If the key already exists in the #GHashTable its current value is replaced
* with the new value. If you supplied a @value_destroy_func when creating the
* #GHashTable, the old value is freed using that function. If you supplied
* a @key_destroy_func when creating the #GHashTable, the passed key is freed
* using that function.
**/
void g_hash_table_insert (GHashTable *hash_table,
gpointer key,
gpointer value)
{
g_hash_table_insert_internal (hash_table, key, value, FALSE);
}
/*
* g_hash_table_lookup_node:
* @hash_table: our #GHashTable
* @key: the key to lookup against
* @hash_return: optional key hash return location
* Return value: index of the described #GHashNode
*
* Performs a lookup in the hash table. Virtually all hash operations
* will use this function internally.
*
* This function first computes the hash value of the key using the
* user's hash function.
*
* If an entry in the table matching @key is found then this function
* returns the index of that entry in the table, and if not, the
* index of an empty node (never a tombstone).
*/
static inline guint g_hash_table_lookup_node (GHashTable *hash_table,
gconstpointer key)
{
GHashNode *node;
guint node_index;
guint hash_value;
guint step = 0;
/* Empty buckets have hash_value set to 0, and for tombstones, it's 1.
* We need to make sure our hash value is not one of these. */
hash_value = (* hash_table->hash_func) (key);
if (hash_value <= 1)
hash_value = 2;
node_index = hash_value % hash_table->mod;
node = &hash_table->nodes [node_index];
while (node->key_hash)
{
/* We first check if our full hash values
* are equal so we can avoid calling the full-blown
* key equality function in most cases.
*/
if (node->key_hash == hash_value)
{
if (hash_table->key_equal_func)
{
if (hash_table->key_equal_func (node->key, key))
break;
}
else if (node->key == key)
{
break;
}
}
step++;
node_index += step;
node_index &= hash_table->mask;
node = &hash_table->nodes [node_index];
}
return node_index;
}
/**
* g_hash_table_lookup:
* @hash_table: a #GHashTable.
* @key: the key to look up.
*
* Looks up a key in a #GHashTable. Note that this function cannot
* distinguish between a key that is not present and one which is present
* and has the value %NULL. If you need this distinction, use
* g_hash_table_lookup_extended().
*
* Return value: the associated value, or %NULL if the key is not found.
**/
gpointer g_hash_table_lookup (GHashTable *hash_table,
gconstpointer key)
{
GHashNode *node;
guint node_index;
if (hash_table == NULL) return NULL;
node_index = g_hash_table_lookup_node (hash_table, key);
node = &hash_table->nodes [node_index];
return node->key_hash ? node->value : NULL;
}
/**
* g_hash_table_new:
* @hash_func: a function to create a hash value from a key.
* Hash values are used to determine where keys are stored within the
* #GHashTable data structure. The g_direct_hash(), g_int_hash(),
* g_int64_hash(), g_double_hash() and g_str_hash() functions are provided
* for some common types of keys.
* If hash_func is %NULL, g_direct_hash() is used.
* @key_equal_func: a function to check two keys for equality. This is
* used when looking up keys in the #GHashTable. The g_direct_equal(),
* g_int_equal(), g_int64_equal(), g_double_equal() and g_str_equal()
* functions are provided for the most common types of keys.
* If @key_equal_func is %NULL, keys are compared directly in a similar
* fashion to g_direct_equal(), but without the overhead of a function call.
*
* Creates a new #GHashTable with a reference count of 1.
*
* Return value: a new #GHashTable.
**/
2016-12-19 14:18:33 +00:00
GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func)
{
return g_hash_table_new_full(hash_func, key_equal_func, NULL, NULL);
}
/**
* g_hash_table_new_full:
* @hash_func: a function to create a hash value from a key.
* @key_equal_func: a function to check two keys for equality.
* @key_destroy_func: a function to free the memory allocated for the key
* used when removing the entry from the #GHashTable or %NULL if you
* don't want to supply such a function.
* @value_destroy_func: a function to free the memory allocated for the
* value used when removing the entry from the #GHashTable or %NULL if
* you don't want to supply such a function.
*
* Creates a new #GHashTable like g_hash_table_new() with a reference count
* of 1 and allows to specify functions to free the memory allocated for the
* key and value that get called when removing the entry from the #GHashTable.
*
* Return value: a new #GHashTable.
**/
GHashTable* g_hash_table_new_full (GHashFunc hash_func,
GEqualFunc key_equal_func,
GDestroyNotify key_destroy_func,
GDestroyNotify value_destroy_func)
{
GHashTable *hash_table;
hash_table = (GHashTable*)g_malloc(sizeof(GHashTable));
//hash_table = g_slice_new (GHashTable);
g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT);
hash_table->nnodes = 0;
hash_table->noccupied = 0;
hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
hash_table->key_equal_func = key_equal_func;
hash_table->ref_count = 1;
hash_table->key_destroy_func = key_destroy_func;
hash_table->value_destroy_func = value_destroy_func;
hash_table->nodes = g_new0 (GHashNode, hash_table->size);
return hash_table;
}
/*
* g_hash_table_remove_all_nodes:
* @hash_table: our #GHashTable
* @notify: %TRUE if the destroy notify handlers are to be called
*
* Removes all nodes from the table. Since this may be a precursor to
* freeing the table entirely, no resize is performed.
*
* If @notify is %TRUE then the destroy notify functions are called
* for the key and value of the hash node.
*/
static void g_hash_table_remove_all_nodes (GHashTable *hash_table,
gboolean notify)
2016-12-19 14:18:33 +00:00
{
int i;
for (i = 0; i < hash_table->size; i++)
{
GHashNode *node = &hash_table->nodes [i];
if (node->key_hash > 1)
{
if (notify && hash_table->key_destroy_func)
hash_table->key_destroy_func (node->key);
if (notify && hash_table->value_destroy_func)
hash_table->value_destroy_func (node->value);
}
}
/* We need to set node->key_hash = 0 for all nodes - might as well be GC
* friendly and clear everything */
memset (hash_table->nodes, 0, hash_table->size * sizeof (GHashNode));
hash_table->nnodes = 0;
hash_table->noccupied = 0;
}
/**
* g_hash_table_remove_all:
* @hash_table: a #GHashTable
*
* Removes all keys and their associated values from a #GHashTable.
*
* If the #GHashTable was created using g_hash_table_new_full(), the keys
* and values are freed using the supplied destroy functions, otherwise you
* have to make sure that any dynamically allocated values are freed
* yourself.
*
* Since: 2.12
**/
void g_hash_table_remove_all (GHashTable *hash_table)
{
if (hash_table == NULL) return;
g_hash_table_remove_all_nodes (hash_table, TRUE);
g_hash_table_maybe_resize (hash_table);
}
/*
* g_hash_table_remove_node:
* @hash_table: our #GHashTable
* @node: pointer to node to remove
* @notify: %TRUE if the destroy notify handlers are to be called
*
* Removes a node from the hash table and updates the node count.
* The node is replaced by a tombstone. No table resize is performed.
*
* If @notify is %TRUE then the destroy notify functions are called
* for the key and value of the hash node.
*/
static void g_hash_table_remove_node (GHashTable *hash_table,
GHashNode *node,
gboolean notify)
{
if (notify && hash_table->key_destroy_func)
hash_table->key_destroy_func (node->key);
if (notify && hash_table->value_destroy_func)
hash_table->value_destroy_func (node->value);
/* Erect tombstone */
node->key_hash = 1;
/* Be GC friendly */
node->key = NULL;
node->value = NULL;
hash_table->nnodes--;
}
/*
* g_hash_table_remove_internal:
* @hash_table: our #GHashTable
* @key: the key to remove
* @notify: %TRUE if the destroy notify handlers are to be called
* Return value: %TRUE if a node was found and removed, else %FALSE
*
* Implements the common logic for the g_hash_table_remove() and
* g_hash_table_steal() functions.
*
* Do a lookup of @key and remove it if it is found, calling the
* destroy notify handlers only if @notify is %TRUE.
*/
static gboolean g_hash_table_remove_internal (GHashTable *hash_table,
gconstpointer key,
gboolean notify)
{
GHashNode *node;
guint node_index;
if (hash_table == NULL) return FALSE;
node_index = g_hash_table_lookup_node (hash_table, key);
node = &hash_table->nodes [node_index];
/* g_hash_table_lookup_node() never returns a tombstone, so this is safe */
if (!node->key_hash)
return FALSE;
g_hash_table_remove_node (hash_table, node, notify);
g_hash_table_maybe_resize (hash_table);
return TRUE;
}
/**
* g_hash_table_remove:
* @hash_table: a #GHashTable.
* @key: the key to remove.
*
* Removes a key and its associated value from a #GHashTable.
*
* If the #GHashTable was created using g_hash_table_new_full(), the
* key and value are freed using the supplied destroy functions, otherwise
* you have to make sure that any dynamically allocated values are freed
* yourself.
*
* Return value: %TRUE if the key was found and removed from the #GHashTable.
**/
gboolean g_hash_table_remove (GHashTable *hash_table,
gconstpointer key)
{
return g_hash_table_remove_internal (hash_table, key, TRUE);
}
/**
* g_hash_table_unref:
* @hash_table: a valid #GHashTable.
*
* Atomically decrements the reference count of @hash_table by one.
* If the reference count drops to 0, all keys and values will be
* destroyed, and all memory allocated by the hash table is released.
* This function is MT-safe and may be called from any thread.
*
* Since: 2.10
**/
void g_hash_table_unref (GHashTable *hash_table)
2016-12-19 14:18:33 +00:00
{
if (hash_table == NULL) return;
if (hash_table->ref_count == 0) return;
hash_table->ref_count--;
if (hash_table->ref_count == 0) {
g_hash_table_remove_all_nodes (hash_table, TRUE);
g_free (hash_table->nodes);
g_free (hash_table);
}
}
/**
* g_hash_table_ref:
* @hash_table: a valid #GHashTable.
*
* Atomically increments the reference count of @hash_table by one.
* This function is MT-safe and may be called from any thread.
*
* Return value: the passed in #GHashTable.
*
* Since: 2.10
**/
GHashTable *g_hash_table_ref (GHashTable *hash_table)
{
if (hash_table == NULL) return NULL;
if (hash_table->ref_count == 0) return hash_table;
//g_atomic_int_add (&hash_table->ref_count, 1);
hash_table->ref_count++;
return hash_table;
}
guint g_hash_table_size(GHashTable *hash_table)
2016-12-19 14:18:33 +00:00
{
if (hash_table == NULL) return 0;
return hash_table->nnodes;
}
typedef struct
{
GHashTable *hash_table;
gpointer dummy1;
gpointer dummy2;
int position;
gboolean dummy3;
int version;
} RealIter;
#define HASH_IS_UNUSED(h_) ((h_) == UNUSED_HASH_VALUE)
#define HASH_IS_TOMBSTONE(h_) ((h_) == TOMBSTONE_HASH_VALUE)
#define HASH_IS_REAL(h_) ((h_) >= 2)
void g_hash_table_iter_init(GHashTableIter *iter, GHashTable *hash_table)
{
RealIter *ri = (RealIter *) iter;
if (iter == NULL) {
return;
}
if (hash_table == NULL) {
return;
}
ri->hash_table = hash_table;
ri->position = -1;
}
gboolean g_hash_table_iter_next(GHashTableIter *iter, gpointer *key, gpointer *value)
{
RealIter *ri = (RealIter *) iter;
GHashNode *node;
gint position;
if (iter == NULL)
{
return FALSE;
}
if (ri->position >= ri->hash_table->size)
{
return FALSE;
}
position = ri->position;
do
{
position++;
if (position >= ri->hash_table->size)
{
ri->position = position;
return FALSE;
}
node = &ri->hash_table->nodes [position];
}
while (node->key_hash <= 1);
if (key != NULL)
*key = node->key;
if (value != NULL)
*value = node->value;
ri->position = position;
return TRUE;
}
GHashTable *g_hash_table_iter_get_hash_table(GHashTableIter *iter)
{
if (iter == NULL) {
return NULL;
}
return ((RealIter *) iter)->hash_table;
}
static void iter_remove_or_steal(RealIter *ri, gboolean notify)
{
if (ri == NULL) {
return;
}
if (ri->position < 0) {
return;
}
if (ri->position >= ri->hash_table->size) {
return;
}
g_hash_table_remove_node (ri->hash_table, &ri->hash_table->nodes[ri->position], notify);
}
void g_hash_table_iter_remove(GHashTableIter *iter)
{
iter_remove_or_steal((RealIter *) iter, TRUE);
}
void g_hash_table_iter_steal(GHashTableIter *iter)
{
iter_remove_or_steal((RealIter *) iter, FALSE);
}
/* END of g_hash_table related functions */
/* general g_XXX substitutes */
void g_free(gpointer ptr)
{
free(ptr);
}
gpointer g_malloc(size_t size)
2016-12-19 14:18:33 +00:00
{
void *res;
if (size == 0) return NULL;
res = malloc(size);
if (res == NULL) exit(1);
return res;
}
gpointer g_malloc0(size_t size)
2016-12-19 14:18:33 +00:00
{
void *res;
if (size == 0) return NULL;
res = calloc(size, 1);
if (res == NULL) exit(1);
return res;
}
gpointer g_try_malloc0(size_t size)
2016-12-19 14:18:33 +00:00
{
if (size == 0) return NULL;
2016-12-26 10:13:46 +00:00
return calloc(size, 1);
}
gpointer g_realloc(gpointer ptr, size_t size)
2016-12-19 14:18:33 +00:00
{
void *res;
if (size == 0) {
free(ptr);
return NULL;
}
res = realloc(ptr, size);
if (res == NULL) exit(1);
return res;
}
2016-12-19 14:18:33 +00:00
char *g_strdup(const char *str)
{
#ifdef _MSC_VER
return str ? _strdup(str) : NULL;
#else
return str ? strdup(str) : NULL;
#endif
}
2016-12-19 14:18:33 +00:00
char *g_strdup_printf(const char *format, ...)
{
va_list ap;
char *res;
va_start(ap, format);
res = g_strdup_vprintf(format, ap);
va_end(ap);
return res;
}
2016-12-19 14:18:33 +00:00
char *g_strdup_vprintf(const char *format, va_list ap)
{
char *str_res = NULL;
#ifdef _MSC_VER
int len = _vscprintf(format, ap);
if( len < 0 )
return NULL;
str_res = (char *)malloc(len+1);
if(str_res==NULL)
return NULL;
vsnprintf(str_res, len+1, format, ap);
#else
vasprintf(&str_res, format, ap);
#endif
return str_res;
}
2016-12-19 14:18:33 +00:00
char *g_strndup(const char *str, size_t n)
{
/* try to mimic glib's g_strndup */
char *res = calloc(n + 1, 1);
strncpy(res, str, n);
return res;
}
2016-12-19 14:18:33 +00:00
void g_strfreev(char **str_array)
{
char **p = str_array;
if (p) {
while (*p) {
free(*p++);
}
}
free(str_array);
}
gpointer g_memdup(gconstpointer mem, size_t byte_size)
2016-12-19 14:18:33 +00:00
{
if (mem) {
void *res = g_malloc(byte_size);
memcpy(res, mem, byte_size);
return res;
}
return NULL;
}
gpointer g_new_(size_t sz, size_t n_structs)
2016-12-19 14:18:33 +00:00
{
size_t need = sz * n_structs;
if ((need / sz) != n_structs) return NULL;
return g_malloc(need);
}
gpointer g_new0_(size_t sz, size_t n_structs)
2016-12-19 14:18:33 +00:00
{
size_t need = sz * n_structs;
if ((need / sz) != n_structs) return NULL;
return g_malloc0(need);
}
gpointer g_renew_(size_t sz, gpointer mem, size_t n_structs)
2016-12-19 14:18:33 +00:00
{
size_t need = sz * n_structs;
if ((need / sz) != n_structs) return NULL;
return g_realloc(mem, need);
}
/**
* g_strconcat:
* @string1: the first string to add, which must not be %NULL
* @Varargs: a %NULL-terminated list of strings to append to the string
*
* Concatenates all of the given strings into one long string.
* The returned string should be freed with g_free() when no longer needed.
*
* Note that this function is usually not the right function to use to
* assemble a translated message from pieces, since proper translation
* often requires the pieces to be reordered.
*
* <warning><para>The variable argument list <emphasis>must</emphasis> end
* with %NULL. If you forget the %NULL, g_strconcat() will start appending
* random memory junk to your string.</para></warning>
*
* Returns: a newly-allocated string containing all the string arguments
*/
gchar* g_strconcat (const gchar *string1, ...)
2016-12-19 14:18:33 +00:00
{
va_list ap;
char *res;
size_t sz = strlen(string1);
va_start(ap, string1);
while (1) {
char *arg = va_arg(ap, char*);
if (arg == NULL) break;
sz += strlen(arg);
}
va_end(ap);
res = g_malloc(sz + 1);
strcpy(res, string1);
va_start(ap, string1);
while (1) {
char *arg = va_arg(ap, char*);
if (arg == NULL) break;
strcat(res, arg);
}
va_end(ap);
return res;
}
/**
* g_strsplit:
* @string: a string to split.
* @delimiter: a string which specifies the places at which to split the string.
* The delimiter is not included in any of the resulting strings, unless
* @max_tokens is reached.
* @max_tokens: the maximum number of pieces to split @string into. If this is
* less than 1, the string is split completely.
*
* Splits a string into a maximum of @max_tokens pieces, using the given
* @delimiter. If @max_tokens is reached, the remainder of @string is appended
* to the last token.
*
* As a special case, the result of splitting the empty string "" is an empty
* vector, not a vector containing a single string. The reason for this
* special case is that being able to represent a empty vector is typically
* more useful than consistent handling of empty elements. If you do need
* to represent empty elements, you'll need to check for the empty string
* before calling g_strsplit().
*
* Return value: a newly-allocated %NULL-terminated array of strings. Use
* g_strfreev() to free it.
**/
gchar** g_strsplit (const gchar *string,
const gchar *delimiter,
gint max_tokens)
2016-12-19 14:18:33 +00:00
{
GSList *string_list = NULL, *slist;
gchar **str_array, *s;
guint n = 0;
const gchar *remainder;
if (string == NULL) return NULL;
if (delimiter == NULL) return NULL;
if (delimiter[0] == '\0') return NULL;
if (max_tokens < 1)
max_tokens = G_MAXINT;
remainder = string;
s = strstr (remainder, delimiter);
if (s)
{
gsize delimiter_len = strlen (delimiter);
while (--max_tokens && s)
{
gsize len;
len = s - remainder;
string_list = g_slist_prepend (string_list,
g_strndup (remainder, len));
n++;
remainder = s + delimiter_len;
s = strstr (remainder, delimiter);
}
}
if (*string)
{
n++;
string_list = g_slist_prepend (string_list, g_strdup (remainder));
}
str_array = g_new (gchar*, n + 1);
str_array[n--] = NULL;
for (slist = string_list; slist; slist = slist->next)
str_array[n--] = slist->data;
g_slist_free (string_list);
return str_array;
}
static const char base64_alphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static gsize g_base64_encode_step(const guchar *in, gsize len,
gboolean break_lines,
gchar *out, gint *state,
gint *save)
{
char *outptr;
const guchar *inptr;
if (in == NULL || out == NULL || state == NULL || save == NULL) {
return 0;
}
if (len <= 0) {
return 0;
}
inptr = in;
outptr = out;
if (len + ((char *) save) [0] > 2)
{
const guchar *inend = in + len - 2;
int c1, c2, c3;
int already;
already = *state;
switch (((char *) save)[0])
{
case 1:
c1 = ((unsigned char *) save)[1];
goto skip1;
case 2:
c1 = ((unsigned char *) save)[1];
c2 = ((unsigned char *) save)[2];
goto skip2;
}
/*
* yes, we jump into the loop, no i'm not going to change it,
* it's beautiful!
*/
while (inptr < inend)
{
c1 = *inptr++;
skip1:
c2 = *inptr++;
skip2:
c3 = *inptr++;
*outptr++ = base64_alphabet[c1 >> 2];
*outptr++ = base64_alphabet[c2 >> 4 | ((c1 & 0x3) << 4)];
*outptr++ = base64_alphabet[((c2 & 0x0f) << 2) | (c3 >> 6)];
*outptr++ = base64_alphabet[c3 & 0x3f];
/* this is a bit ugly ... */
if (break_lines && (++already) >= 19)
{
*outptr++ = '\n';
already = 0;
}
}
((char *)save)[0] = 0;
len = 2 - (inptr - inend);
*state = already;
}
if (len > 0)
{
char *saveout;
/* points to the slot for the next char to save */
saveout = & (((char *)save)[1]) + ((char *)save)[0];
/* len can only be 0 1 or 2 */
switch (len)
{
case 2: *saveout++ = *inptr++;
case 1: *saveout++ = *inptr++;
}
((char *) save)[0] += len;
}
return outptr - out;
}
gsize g_base64_encode_close(gboolean break_lines, gchar *out,
gint *state, gint *save)
{
int c1, c2;
char *outptr = out;
if (out == NULL || state == NULL || save == NULL) {
return 0;
}
c1 = ((unsigned char *) save)[1];
c2 = ((unsigned char *) save)[2];
switch (((char *) save)[0])
{
case 2:
outptr[2] = base64_alphabet[((c2 &0x0f) << 2)];
g_assert(outptr[2] != 0);
goto skip;
case 1:
outptr[2] = '=';
c2 = 0; /* saved state here is not relevant */
skip:
outptr[0] = base64_alphabet[c1 >> 2 ];
outptr[1] = base64_alphabet[c2 >> 4 | ((c1 & 0x3) << 4)];
outptr[3] = '=';
outptr += 4;
break;
}
if (break_lines) {
*outptr++ = '\n';
}
*save = 0;
*state = 0;
return outptr - out;
}
gchar *g_base64_encode(const guchar *data, gsize len)
{
gchar *out;
gint state = 0, outlen;
gint save = 0;
if (data == NULL && len != 0) {
return NULL;
}
/* We can use a smaller limit here, since we know the saved state is 0,
+1 is needed for trailing \0, also check for unlikely integer overflow */
if (len >= ((SIZE_MAX - 1) / 4 - 1) * 3) {
//g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)",
// G_STRLOC, len);
return NULL;
}
out = g_malloc((len / 3 + 1) * 4 + 1);
outlen = g_base64_encode_step(data, len, FALSE, out, &state, &save);
outlen += g_base64_encode_close(FALSE, out + outlen, &state, &save);
out[outlen] = '\0';
return (gchar *) out;
}
static const unsigned char mime_base64_rank[256] = {
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
};
static gsize g_base64_decode_step(const gchar *in, gsize len,
guchar *out, gint *state,
guint *save)
{
const guchar *inptr;
guchar *outptr;
const guchar *inend;
guchar c, rank;
guchar last[2];
unsigned int v;
int i;
if (in == NULL || out == NULL || state == NULL || save == NULL) {
return 0;
}
if (len <= 0) {
return 0;
}
inend = (const guchar *)in+len;
outptr = out;
/* convert 4 base64 bytes to 3 normal bytes */
v = *save;
i = *state;
last[0] = last[1] = 0;
/* we use the sign in the state to determine if we got a padding character
in the previous sequence */
if (i < 0)
{
i = -i;
last[0] = '=';
}
inptr = (const guchar *)in;
while (inptr < inend)
{
c = *inptr++;
rank = mime_base64_rank[c];
if (rank != 0xff)
{
last[1] = last[0];
last[0] = c;
v = (v << 6) | rank;
i++;
if (i == 4)
{
*outptr++ = v >> 16;
if (last[1] != '=') {
*outptr++ = v >> 8;
}
if (last[0] != '=') {
*outptr++ = v;
}
i = 0;
}
}
}
*save = v;
*state = last[0] == '=' ? -i : i;
return outptr - out;
}
guchar *g_base64_decode(const gchar *text, gsize *out_len)
{
guchar *ret;
gsize input_length;
gint state = 0;
guint save = 0;
if (text == NULL || out_len == NULL) {
return NULL;
}
input_length = strlen(text);
/* We can use a smaller limit here, since we know the saved state is 0,
+1 used to avoid calling g_malloc0(0), and hence returning NULL */
ret = g_malloc0((input_length / 4) * 3 + 1);
*out_len = g_base64_decode_step(text, input_length, ret, &state, &save);
return ret;
}
guchar *g_base64_decode_inplace(gchar *text, gsize *out_len)
{
gint input_length, state = 0;
guint save = 0;
if (text == NULL || out_len == NULL) {
return NULL;
}
input_length = strlen(text);
if (input_length <= 1) {
return NULL;
}
*out_len = g_base64_decode_step(text, input_length, (guchar *) text, &state, &save);
return (guchar *) text;
}