mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
c9bd7542e1
Wed Jul 26 12:59:31 2000 Tim Janik <timj@gtk.org> * *.[hc]: applied patch from Andreas Persenius <ndap@swipnet.se> that updates the license headers to the GNU Lesser General Public License, as well as updating the copyright year to 2000.
460 lines
10 KiB
C
460 lines
10 KiB
C
/* GLIB - Library of useful routines for C programming
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GLib Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
/*
|
|
* MT safe
|
|
*/
|
|
|
|
#include "glib.h"
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
typedef struct _GRealRelation GRealRelation;
|
|
typedef struct _GRealTuples GRealTuples;
|
|
|
|
struct _GRealRelation
|
|
{
|
|
gint fields;
|
|
gint current_field;
|
|
|
|
GHashTable *all_tuples;
|
|
GHashTable **hashed_tuple_tables;
|
|
GMemChunk *tuple_chunk;
|
|
|
|
gint count;
|
|
};
|
|
|
|
struct _GRealTuples
|
|
{
|
|
gint len;
|
|
gint width;
|
|
gpointer *data;
|
|
};
|
|
|
|
static gboolean
|
|
tuple_equal_2 (gconstpointer v_a,
|
|
gconstpointer v_b)
|
|
{
|
|
gpointer* a = (gpointer*) v_a;
|
|
gpointer* b = (gpointer*) v_b;
|
|
|
|
return a[0] == b[0] && a[1] == b[1];
|
|
}
|
|
|
|
static guint
|
|
tuple_hash_2 (gconstpointer v_a)
|
|
{
|
|
gpointer* a = (gpointer*) v_a;
|
|
|
|
return (gulong)a[0] ^ (gulong)a[1];
|
|
}
|
|
|
|
static GHashFunc
|
|
tuple_hash (gint fields)
|
|
{
|
|
switch (fields)
|
|
{
|
|
case 2:
|
|
return tuple_hash_2;
|
|
default:
|
|
g_error ("no tuple hash for %d", fields);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GCompareFunc
|
|
tuple_equal (gint fields)
|
|
{
|
|
switch (fields)
|
|
{
|
|
case 2:
|
|
return tuple_equal_2;
|
|
default:
|
|
g_error ("no tuple equal for %d", fields);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GRelation*
|
|
g_relation_new (gint fields)
|
|
{
|
|
GRealRelation* rel = g_new0 (GRealRelation, 1);
|
|
|
|
rel->fields = fields;
|
|
rel->tuple_chunk = g_mem_chunk_new ("Relation Chunk",
|
|
fields * sizeof (gpointer),
|
|
fields * sizeof (gpointer) * 128,
|
|
G_ALLOC_AND_FREE);
|
|
rel->all_tuples = g_hash_table_new (tuple_hash (fields), tuple_equal (fields));
|
|
rel->hashed_tuple_tables = g_new0 (GHashTable*, fields);
|
|
|
|
return (GRelation*) rel;
|
|
}
|
|
|
|
static void
|
|
g_relation_free_array (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
g_hash_table_destroy ((GHashTable*) value);
|
|
}
|
|
|
|
void
|
|
g_relation_destroy (GRelation *relation)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
gint i;
|
|
|
|
if (rel)
|
|
{
|
|
g_hash_table_destroy (rel->all_tuples);
|
|
g_mem_chunk_destroy (rel->tuple_chunk);
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
{
|
|
if (rel->hashed_tuple_tables[i])
|
|
{
|
|
g_hash_table_foreach (rel->hashed_tuple_tables[i], g_relation_free_array, NULL);
|
|
g_hash_table_destroy (rel->hashed_tuple_tables[i]);
|
|
}
|
|
}
|
|
|
|
g_free (rel->hashed_tuple_tables);
|
|
g_free (rel);
|
|
}
|
|
}
|
|
|
|
void
|
|
g_relation_index (GRelation *relation,
|
|
gint field,
|
|
GHashFunc hash_func,
|
|
GCompareFunc key_compare_func)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
|
|
g_return_if_fail (relation != NULL);
|
|
|
|
g_return_if_fail (rel->count == 0 && rel->hashed_tuple_tables[field] == NULL);
|
|
|
|
rel->hashed_tuple_tables[field] = g_hash_table_new (hash_func, key_compare_func);
|
|
}
|
|
|
|
void
|
|
g_relation_insert (GRelation *relation,
|
|
...)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
gpointer* tuple = g_chunk_new (gpointer, rel->tuple_chunk);
|
|
va_list args;
|
|
gint i;
|
|
|
|
va_start(args, relation);
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
tuple[i] = va_arg(args, gpointer);
|
|
|
|
va_end(args);
|
|
|
|
g_hash_table_insert (rel->all_tuples, tuple, tuple);
|
|
|
|
rel->count += 1;
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
{
|
|
GHashTable *table;
|
|
gpointer key;
|
|
GHashTable *per_key_table;
|
|
|
|
table = rel->hashed_tuple_tables[i];
|
|
|
|
if (table == NULL)
|
|
continue;
|
|
|
|
key = tuple[i];
|
|
per_key_table = g_hash_table_lookup (table, key);
|
|
|
|
if (per_key_table == NULL)
|
|
{
|
|
per_key_table = g_hash_table_new (tuple_hash (rel->fields), tuple_equal (rel->fields));
|
|
g_hash_table_insert (table, key, per_key_table);
|
|
}
|
|
|
|
g_hash_table_insert (per_key_table, tuple, tuple);
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_relation_delete_tuple (gpointer tuple_key,
|
|
gpointer tuple_value,
|
|
gpointer user_data)
|
|
{
|
|
gpointer *tuple = (gpointer*) tuple_value;
|
|
GRealRelation *rel = (GRealRelation *) user_data;
|
|
gint j;
|
|
|
|
g_assert (tuple_key == tuple_value);
|
|
|
|
for (j = 0; j < rel->fields; j += 1)
|
|
{
|
|
GHashTable *one_table = rel->hashed_tuple_tables[j];
|
|
gpointer one_key;
|
|
GHashTable *per_key_table;
|
|
|
|
if (one_table == NULL)
|
|
continue;
|
|
|
|
if (j == rel->current_field)
|
|
/* can't delete from the table we're foreaching in */
|
|
continue;
|
|
|
|
one_key = tuple[j];
|
|
|
|
per_key_table = g_hash_table_lookup (one_table, one_key);
|
|
|
|
g_hash_table_remove (per_key_table, tuple);
|
|
}
|
|
|
|
g_hash_table_remove (rel->all_tuples, tuple);
|
|
|
|
rel->count -= 1;
|
|
}
|
|
|
|
gint
|
|
g_relation_delete (GRelation *relation,
|
|
gconstpointer key,
|
|
gint field)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
GHashTable *table = rel->hashed_tuple_tables[field];
|
|
GHashTable *key_table;
|
|
gint count = rel->count;
|
|
|
|
g_return_val_if_fail (relation != NULL, 0);
|
|
g_return_val_if_fail (table != NULL, 0);
|
|
|
|
key_table = g_hash_table_lookup (table, key);
|
|
|
|
if (!key_table)
|
|
return 0;
|
|
|
|
rel->current_field = field;
|
|
|
|
g_hash_table_foreach (key_table, g_relation_delete_tuple, rel);
|
|
|
|
g_hash_table_remove (table, key);
|
|
|
|
g_hash_table_destroy (key_table);
|
|
|
|
/* @@@ FIXME: Remove empty hash tables. */
|
|
|
|
return count - rel->count;
|
|
}
|
|
|
|
static void
|
|
g_relation_select_tuple (gpointer tuple_key,
|
|
gpointer tuple_value,
|
|
gpointer user_data)
|
|
{
|
|
gpointer *tuple = (gpointer*) tuple_value;
|
|
GRealTuples *tuples = (GRealTuples*) user_data;
|
|
gint stride = sizeof (gpointer) * tuples->width;
|
|
|
|
g_assert (tuple_key == tuple_value);
|
|
|
|
memcpy (tuples->data + (tuples->len * tuples->width),
|
|
tuple,
|
|
stride);
|
|
|
|
tuples->len += 1;
|
|
}
|
|
|
|
GTuples*
|
|
g_relation_select (GRelation *relation,
|
|
gconstpointer key,
|
|
gint field)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
GHashTable *table = rel->hashed_tuple_tables[field];
|
|
GHashTable *key_table;
|
|
GRealTuples *tuples = g_new0 (GRealTuples, 1);
|
|
gint count;
|
|
|
|
g_return_val_if_fail (relation != NULL, NULL);
|
|
g_return_val_if_fail (table != NULL, NULL);
|
|
|
|
key_table = g_hash_table_lookup (table, key);
|
|
|
|
if (!key_table)
|
|
return (GTuples*)tuples;
|
|
|
|
count = g_relation_count (relation, key, field);
|
|
|
|
tuples->data = g_malloc (sizeof (gpointer) * rel->fields * count);
|
|
tuples->width = rel->fields;
|
|
|
|
g_hash_table_foreach (key_table, g_relation_select_tuple, tuples);
|
|
|
|
g_assert (count == tuples->len);
|
|
|
|
return (GTuples*)tuples;
|
|
}
|
|
|
|
gint
|
|
g_relation_count (GRelation *relation,
|
|
gconstpointer key,
|
|
gint field)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
GHashTable *table = rel->hashed_tuple_tables[field];
|
|
GHashTable *key_table;
|
|
|
|
g_return_val_if_fail (relation != NULL, 0);
|
|
g_return_val_if_fail (table != NULL, 0);
|
|
|
|
key_table = g_hash_table_lookup (table, key);
|
|
|
|
if (!key_table)
|
|
return 0;
|
|
|
|
return g_hash_table_size (key_table);
|
|
}
|
|
|
|
gboolean
|
|
g_relation_exists (GRelation *relation, ...)
|
|
{
|
|
GRealRelation *rel = (GRealRelation *) relation;
|
|
gpointer* tuple = g_chunk_new (gpointer, rel->tuple_chunk);
|
|
va_list args;
|
|
gint i;
|
|
gboolean result;
|
|
|
|
va_start(args, relation);
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
tuple[i] = va_arg(args, gpointer);
|
|
|
|
va_end(args);
|
|
|
|
result = g_hash_table_lookup (rel->all_tuples, tuple) != NULL;
|
|
|
|
g_mem_chunk_free (rel->tuple_chunk, tuple);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
g_tuples_destroy (GTuples *tuples0)
|
|
{
|
|
GRealTuples *tuples = (GRealTuples*) tuples0;
|
|
|
|
if (tuples)
|
|
{
|
|
g_free (tuples->data);
|
|
g_free (tuples);
|
|
}
|
|
}
|
|
|
|
gpointer
|
|
g_tuples_index (GTuples *tuples0,
|
|
gint index,
|
|
gint field)
|
|
{
|
|
GRealTuples *tuples = (GRealTuples*) tuples0;
|
|
|
|
g_return_val_if_fail (tuples0 != NULL, NULL);
|
|
g_return_val_if_fail (field < tuples->width, NULL);
|
|
|
|
return tuples->data[index * tuples->width + field];
|
|
}
|
|
|
|
/* Print
|
|
*/
|
|
|
|
static void
|
|
g_relation_print_one (gpointer tuple_key,
|
|
gpointer tuple_value,
|
|
gpointer user_data)
|
|
{
|
|
gint i;
|
|
GString *gstring;
|
|
GRealRelation* rel = (GRealRelation*) user_data;
|
|
gpointer* tuples = (gpointer*) tuple_value;
|
|
|
|
gstring = g_string_new ("[");
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
{
|
|
g_string_sprintfa (gstring, "%p", tuples[i]);
|
|
|
|
if (i < (rel->fields - 1))
|
|
g_string_append (gstring, ",");
|
|
}
|
|
|
|
g_string_append (gstring, "]");
|
|
g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, gstring->str);
|
|
g_string_free (gstring, TRUE);
|
|
}
|
|
|
|
static void
|
|
g_relation_print_index (gpointer tuple_key,
|
|
gpointer tuple_value,
|
|
gpointer user_data)
|
|
{
|
|
GRealRelation* rel = (GRealRelation*) user_data;
|
|
GHashTable* table = (GHashTable*) tuple_value;
|
|
|
|
g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "*** key %p", tuple_key);
|
|
|
|
g_hash_table_foreach (table,
|
|
g_relation_print_one,
|
|
rel);
|
|
}
|
|
|
|
void
|
|
g_relation_print (GRelation *relation)
|
|
{
|
|
gint i;
|
|
GRealRelation* rel = (GRealRelation*) relation;
|
|
|
|
g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "*** all tuples (%d)", rel->count);
|
|
|
|
g_hash_table_foreach (rel->all_tuples,
|
|
g_relation_print_one,
|
|
rel);
|
|
|
|
for (i = 0; i < rel->fields; i += 1)
|
|
{
|
|
if (rel->hashed_tuple_tables[i] == NULL)
|
|
continue;
|
|
|
|
g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "*** index %d", i);
|
|
|
|
g_hash_table_foreach (rel->hashed_tuple_tables[i],
|
|
g_relation_print_index,
|
|
rel);
|
|
}
|
|
|
|
}
|