mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-25 07:15:27 +00:00
c14d8226ab
Commit 7f8f9ef1 introduced the ability to store a list of integers as a sorted list of ranges, but when merging ranges, it leaks one or more ranges. It was also using range_get_last() incorrectly within range_compare() (a range is a start/end pair, but range_get_last() is for start/len pairs), and will also mishandle a range ending in UINT64_MAX (remember, we document that no range covers 2**64 bytes, but that ranges that end on UINT64_MAX have end < begin). The whole merge algorithm was rather complex, and included unnecessary passes over data within glib functions, and enough indirection to make it hard to easily plug the data leaks. Since we are already hard-coding things to a list of ranges, just rewrite the thing to open-code the traversal and comparisons, by making the range_compare() helper function give us an answer that is easier to use, at which point we avoid the need to pass any callbacks to g_list_*(). Then by reusing range_extend() instead of duplicating effort with range_merge(), we cover the corner cases correctly. Drop the now-unused range_merge() and ranges_can_merge(). Doing this lets test-string-{input,output}-visitor pass under valgrind without leaks. Backports commit db486cc334aafd3dbdaf107388e37fc3d6d3e171 from qemu
1941 lines
48 KiB
C
1941 lines
48 KiB
C
/*
|
|
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.
|
|
*/
|
|
|
|
// 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))
|
|
#ifndef _WIN64
|
|
#define GPOINTER_TO_UINT(p) ((guint)(uintptr_t)(p))
|
|
#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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
return *(const gint*) v;
|
|
}
|
|
|
|
gboolean g_int_equal(gconstpointer v1, gconstpointer v2)
|
|
{
|
|
return *((const gint*)v1) == *((const gint*)v2);
|
|
}
|
|
|
|
/* Doubly-linked list */
|
|
|
|
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)
|
|
{
|
|
GList *lp;
|
|
for (lp = list; lp; lp = lp->next) {
|
|
(*func)(lp->data, user_data);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
GList *n = (GList*)g_malloc(sizeof(GList));
|
|
n->next = list;
|
|
n->prev = NULL;
|
|
n->data = data;
|
|
return n;
|
|
}
|
|
|
|
GList *g_list_remove_link(GList *list, GList *llink)
|
|
{
|
|
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)
|
|
{
|
|
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 < @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)
|
|
{
|
|
GSList *head = list;
|
|
if (list) {
|
|
while (list->next) list = list->next;
|
|
list->next = (GSList*)g_malloc(sizeof(GSList));
|
|
list = list->next;
|
|
} 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)
|
|
{
|
|
GSList *lp;
|
|
for (lp = list; lp; lp = lp->next) {
|
|
(*func)(lp->data, user_data);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
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)
|
|
{
|
|
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().
|
|
**/
|
|
void g_hash_table_foreach (GHashTable *hash_table,
|
|
GHFunc func,
|
|
gpointer user_data)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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.
|
|
**/
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
void *res;
|
|
if (size == 0) return NULL;
|
|
res = malloc(size);
|
|
if (res == NULL) exit(1);
|
|
return res;
|
|
}
|
|
|
|
gpointer g_malloc0(size_t size)
|
|
{
|
|
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)
|
|
{
|
|
if (size == 0) return NULL;
|
|
return calloc(size, 1);
|
|
}
|
|
|
|
gpointer g_realloc(gpointer ptr, size_t size)
|
|
{
|
|
void *res;
|
|
if (size == 0) {
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
res = realloc(ptr, size);
|
|
if (res == NULL) exit(1);
|
|
return res;
|
|
}
|
|
|
|
char *g_strdup(const char *str)
|
|
{
|
|
#ifdef _MSC_VER
|
|
return str ? _strdup(str) : NULL;
|
|
#else
|
|
return str ? strdup(str) : NULL;
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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, ...)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|