|
Packit |
0021fb |
/*
|
|
Packit |
0021fb |
* This file is part of ltrace.
|
|
Packit |
0021fb |
* Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is free software; you can redistribute it and/or
|
|
Packit |
0021fb |
* modify it under the terms of the GNU General Public License as
|
|
Packit |
0021fb |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit |
0021fb |
* License, or (at your option) any later version.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
0021fb |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
0021fb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
0021fb |
* General Public License for more details.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* You should have received a copy of the GNU General Public License
|
|
Packit |
0021fb |
* along with this program; if not, write to the Free Software
|
|
Packit |
0021fb |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
Packit |
0021fb |
* 02110-1301 USA
|
|
Packit |
0021fb |
*/
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#include <string.h>
|
|
Packit |
0021fb |
#include <stdlib.h>
|
|
Packit |
0021fb |
#include <stdio.h>
|
|
Packit |
0021fb |
#include "dict.h"
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct status_bits {
|
|
Packit |
0021fb |
unsigned char taken : 1;
|
|
Packit |
0021fb |
unsigned char erased : 1;
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static struct status_bits *
|
|
Packit |
0021fb |
bitp(struct dict *dict, size_t n)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return VECT_ELEMENT(&dict->status, struct status_bits, n);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void
|
|
Packit |
0021fb |
dict_init(struct dict *dict,
|
|
Packit |
0021fb |
size_t key_size, size_t value_size,
|
|
Packit |
0021fb |
size_t (*hash1)(const void *),
|
|
Packit |
0021fb |
int (*eq)(const void *, const void *),
|
|
Packit |
0021fb |
size_t (*hash2)(size_t))
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert(hash1 != NULL);
|
|
Packit |
0021fb |
assert(eq != NULL);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
vect_init(&dict->keys, key_size);
|
|
Packit |
0021fb |
vect_init(&dict->values, value_size);
|
|
Packit |
0021fb |
VECT_INIT(&dict->status, struct status_bits);
|
|
Packit |
0021fb |
dict->size = 0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
dict->hash1 = hash1;
|
|
Packit |
0021fb |
dict->hash2 = hash2;
|
|
Packit |
0021fb |
dict->eq = eq;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct clone_data {
|
|
Packit |
0021fb |
struct dict *target;
|
|
Packit |
0021fb |
int (*clone_key)(void *tgt, const void *src, void *data);
|
|
Packit |
0021fb |
int (*clone_value)(void *tgt, const void *src, void *data);
|
|
Packit |
0021fb |
void (*dtor_key)(void *tgt, void *data);
|
|
Packit |
0021fb |
void (*dtor_value)(void *tgt, void *data);
|
|
Packit |
0021fb |
void *data;
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static enum callback_status
|
|
Packit |
0021fb |
clone_cb(void *key, void *value, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
struct clone_data *clone_data = data;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
char nkey[clone_data->target->keys.elt_size];
|
|
Packit |
0021fb |
if (clone_data->clone_key == NULL)
|
|
Packit |
0021fb |
memmove(nkey, key, sizeof(nkey));
|
|
Packit |
0021fb |
else if (clone_data->clone_key(&nkey, key, clone_data->data) < 0)
|
|
Packit |
0021fb |
return CBS_STOP;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
char nvalue[clone_data->target->values.elt_size];
|
|
Packit |
0021fb |
if (clone_data->clone_value == NULL) {
|
|
Packit |
0021fb |
memmove(nvalue, value, sizeof(nvalue));
|
|
Packit |
0021fb |
} else if (clone_data->clone_value(&nvalue, value,
|
|
Packit |
0021fb |
clone_data->data) < 0) {
|
|
Packit |
0021fb |
fail:
|
|
Packit |
0021fb |
if (clone_data->clone_key != NULL)
|
|
Packit |
0021fb |
clone_data->dtor_key(&nkey, clone_data->data);
|
|
Packit |
0021fb |
return CBS_STOP;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (dict_insert(clone_data->target, nkey, nvalue) < 0) {
|
|
Packit |
0021fb |
if (clone_data->clone_value != NULL)
|
|
Packit |
0021fb |
clone_data->dtor_value(&nvalue, clone_data->data);
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return CBS_CONT;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_clone(struct dict *target, const struct dict *source,
|
|
Packit |
0021fb |
int (*clone_key)(void *tgt, const void *src, void *data),
|
|
Packit |
0021fb |
void (*dtor_key)(void *tgt, void *data),
|
|
Packit |
0021fb |
int (*clone_value)(void *tgt, const void *src, void *data),
|
|
Packit |
0021fb |
void (*dtor_value)(void *tgt, void *data),
|
|
Packit |
0021fb |
void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert((clone_key != NULL) == (dtor_key != NULL));
|
|
Packit |
0021fb |
assert((clone_value != NULL) == (dtor_value != NULL));
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
dict_init(target, source->keys.elt_size, source->values.elt_size,
|
|
Packit |
0021fb |
source->hash1, source->eq, source->hash2);
|
|
Packit |
0021fb |
struct clone_data clone_data = {
|
|
Packit |
0021fb |
target, clone_key, clone_value, dtor_key, dtor_value, data
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
if (dict_each((struct dict *)source, NULL,
|
|
Packit |
0021fb |
clone_cb, &clone_data) != NULL) {
|
|
Packit |
0021fb |
dict_destroy(target, dtor_key, dtor_value, data);
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
size_t
|
|
Packit |
0021fb |
dict_size(const struct dict *dict)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return dict->size;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_empty(const struct dict *dict)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return dict->size == 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct destroy_data {
|
|
Packit |
0021fb |
void (*dtor_key)(void *tgt, void *data);
|
|
Packit |
0021fb |
void (*dtor_value)(void *tgt, void *data);
|
|
Packit |
0021fb |
void *data;
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static enum callback_status
|
|
Packit |
0021fb |
destroy_cb(void *key, void *value, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
struct destroy_data *destroy_data = data;
|
|
Packit |
0021fb |
if (destroy_data->dtor_key)
|
|
Packit |
0021fb |
destroy_data->dtor_key(key, destroy_data->data);
|
|
Packit |
0021fb |
if (destroy_data->dtor_value)
|
|
Packit |
0021fb |
destroy_data->dtor_value(value, destroy_data->data);
|
|
Packit |
0021fb |
return CBS_CONT;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void
|
|
Packit |
0021fb |
dict_destroy(struct dict *dict,
|
|
Packit |
0021fb |
void (*dtor_key)(void *tgt, void *data),
|
|
Packit |
0021fb |
void (*dtor_value)(void *tgt, void *data),
|
|
Packit |
0021fb |
void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
/* Some slots are unused (the corresponding keys and values
|
|
Packit |
0021fb |
* are uninitialized), so we can't call dtors for them.
|
|
Packit |
0021fb |
* Iterate DICT instead. */
|
|
Packit |
0021fb |
if (dtor_key != NULL || dtor_value != NULL) {
|
|
Packit |
0021fb |
struct destroy_data destroy_data = {
|
|
Packit |
0021fb |
dtor_key, dtor_value, data
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
dict_each(dict, NULL, destroy_cb, &destroy_data);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
vect_destroy(&dict->keys, NULL, NULL);
|
|
Packit |
0021fb |
vect_destroy(&dict->values, NULL, NULL);
|
|
Packit |
0021fb |
vect_destroy(&dict->status, NULL, NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
default_secondary_hash(size_t pos)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return pos % 97 + 1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
small_secondary_hash(size_t pos)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return 1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static inline size_t
|
|
Packit |
0021fb |
n(struct dict *dict)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return vect_size(&dict->keys);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static inline size_t (*
|
|
Packit |
0021fb |
hash2(struct dict *dict))(size_t)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (dict->hash2 != NULL)
|
|
Packit |
0021fb |
return dict->hash2;
|
|
Packit |
0021fb |
else if (n(dict) < 100)
|
|
Packit |
0021fb |
return small_secondary_hash;
|
|
Packit |
0021fb |
else
|
|
Packit |
0021fb |
return default_secondary_hash;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void *
|
|
Packit |
0021fb |
getkey(struct dict *dict, size_t pos)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return ((unsigned char *)dict->keys.data)
|
|
Packit |
0021fb |
+ dict->keys.elt_size * pos;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void *
|
|
Packit |
0021fb |
getvalue(struct dict *dict, size_t pos)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return ((unsigned char *)dict->values.data)
|
|
Packit |
0021fb |
+ dict->values.elt_size * pos;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
find_slot(struct dict *dict, const void *key,
|
|
Packit |
0021fb |
int *foundp, int *should_rehash, size_t *pi)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert(n(dict) > 0);
|
|
Packit |
0021fb |
size_t pos = dict->hash1(key) % n(dict);
|
|
Packit |
0021fb |
size_t pos0 = -1;
|
|
Packit |
0021fb |
size_t d = hash2(dict)(pos);
|
|
Packit |
0021fb |
size_t i = 0;
|
|
Packit |
0021fb |
*foundp = 0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* We skip over any taken or erased slots. But we remember
|
|
Packit |
0021fb |
* the first erased that we find, and if we don't find the key
|
|
Packit |
0021fb |
* later, we return that position. */
|
|
Packit |
0021fb |
for (; bitp(dict, pos)->taken || bitp(dict, pos)->erased;
|
|
Packit |
0021fb |
pos = (pos + d) % n(dict)) {
|
|
Packit |
0021fb |
if (pos0 == (size_t)-1 && bitp(dict, pos)->erased)
|
|
Packit |
0021fb |
pos0 = pos;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* If there is a loop, but we've seen an erased
|
|
Packit |
0021fb |
* element, take that one. Otherwise give up. */
|
|
Packit |
0021fb |
if (++i > dict->size) {
|
|
Packit |
0021fb |
if (pos0 != (size_t)-1)
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
return (size_t)-1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (bitp(dict, pos)->taken
|
|
Packit |
0021fb |
&& dict->eq(getkey(dict, pos), key)) {
|
|
Packit |
0021fb |
*foundp = 1;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (!*foundp && pos0 != (size_t)-1)
|
|
Packit |
0021fb |
pos = pos0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* If the hash table degraded into a linked list, request a
|
|
Packit |
0021fb |
* rehash. */
|
|
Packit |
0021fb |
if (should_rehash != NULL)
|
|
Packit |
0021fb |
*should_rehash = i > 10 && i > n(dict) / 10;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (pi != NULL)
|
|
Packit |
0021fb |
*pi = i;
|
|
Packit |
0021fb |
return pos;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static enum callback_status
|
|
Packit |
0021fb |
rehash_move(void *key, void *value, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (dict_insert(data, key, value) < 0)
|
|
Packit |
0021fb |
return CBS_STOP;
|
|
Packit |
0021fb |
else
|
|
Packit |
0021fb |
return CBS_CONT;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static int
|
|
Packit |
0021fb |
rehash(struct dict *dict, size_t nn)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert(nn != n(dict));
|
|
Packit |
0021fb |
int ret = -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct dict tmp;
|
|
Packit |
0021fb |
dict_init(&tmp, dict->keys.elt_size, dict->values.elt_size,
|
|
Packit |
0021fb |
dict->hash1, dict->eq, dict->hash2);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* To honor all invariants (so that we can safely call
|
|
Packit |
0021fb |
* dict_destroy), we first make a request to _reserve_ enough
|
|
Packit |
0021fb |
* room in all vectors. This has no observable effect on
|
|
Packit |
0021fb |
* contents of vectors. */
|
|
Packit |
0021fb |
if (vect_reserve(&tmp.keys, nn) < 0
|
|
Packit |
0021fb |
|| vect_reserve(&tmp.values, nn) < 0
|
|
Packit |
0021fb |
|| vect_reserve(&tmp.status, nn) < 0)
|
|
Packit |
0021fb |
goto done;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* Now that we know that there is enough size in vectors, we
|
|
Packit |
0021fb |
* simply bump the size. */
|
|
Packit |
0021fb |
tmp.keys.size = nn;
|
|
Packit |
0021fb |
tmp.values.size = nn;
|
|
Packit |
0021fb |
size_t old_size = tmp.status.size;
|
|
Packit |
0021fb |
tmp.status.size = nn;
|
|
Packit |
0021fb |
memset(VECT_ELEMENT(&tmp.status, struct status_bits, old_size),
|
|
Packit |
0021fb |
0, (tmp.status.size - old_size) * tmp.status.elt_size);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* At this point, TMP is once more an empty dictionary with NN
|
|
Packit |
0021fb |
* slots. Now move stuff from DICT to TMP. */
|
|
Packit |
0021fb |
if (dict_each(dict, NULL, rehash_move, &tmp) != NULL)
|
|
Packit |
0021fb |
goto done;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* And now swap contents of DICT and TMP, and we are done. */
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
struct dict tmp2 = *dict;
|
|
Packit |
0021fb |
*dict = tmp;
|
|
Packit |
0021fb |
tmp = tmp2;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
ret = 0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
done:
|
|
Packit |
0021fb |
/* We only want to release the containers, not the actual data
|
|
Packit |
0021fb |
* that they hold, so it's fine if we don't pass any dtor. */
|
|
Packit |
0021fb |
dict_destroy(&tmp, NULL, NULL, NULL);
|
|
Packit |
0021fb |
return ret;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static const size_t primes[] = {
|
|
Packit |
0021fb |
13, 31, 61, 127, 251, 509, 1021, 2039, 4093,
|
|
Packit |
0021fb |
8191, 16381, 32749, 65521, 130981, 0
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
larger_size(size_t current)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (current == 0)
|
|
Packit |
0021fb |
return primes[0];
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (current < primes[sizeof(primes)/sizeof(*primes) - 2]) {
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
for (i = 0; primes[i] != 0; ++i)
|
|
Packit |
0021fb |
if (primes[i] > current)
|
|
Packit |
0021fb |
return primes[i];
|
|
Packit |
0021fb |
abort();
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* We ran out of primes, so invent a new one. The following
|
|
Packit |
0021fb |
* gives primes until about 17M elements (and then some more
|
|
Packit |
0021fb |
* later). */
|
|
Packit |
0021fb |
return 2 * current + 6585;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
smaller_size(size_t current)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (current <= primes[0])
|
|
Packit |
0021fb |
return primes[0];
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (current <= primes[sizeof(primes)/sizeof(*primes) - 2]) {
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
size_t prev = 0;
|
|
Packit |
0021fb |
for (i = 0; primes[i] != 0; ++i) {
|
|
Packit |
0021fb |
if (primes[i] >= current)
|
|
Packit |
0021fb |
return prev;
|
|
Packit |
0021fb |
prev = primes[i];
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
abort();
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return (current - 6585) / 2;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_insert(struct dict *dict, void *key, void *value)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (n(dict) == 0 || dict->size > 0.7 * n(dict))
|
|
Packit |
0021fb |
rehash:
|
|
Packit |
0021fb |
if (rehash(dict, larger_size(n(dict))) < 0)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int found;
|
|
Packit |
0021fb |
int should_rehash;
|
|
Packit |
0021fb |
size_t slot_n = find_slot(dict, key, &found, &should_rehash, NULL);
|
|
Packit |
0021fb |
if (slot_n == (size_t)-1)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
if (found)
|
|
Packit |
0021fb |
return 1;
|
|
Packit |
0021fb |
assert(!bitp(dict, slot_n)->taken);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* If rehash was requested, do that, and retry. But just live
|
|
Packit |
0021fb |
* with it for apparently sparse tables. No resizing can fix
|
|
Packit |
0021fb |
* a rubbish hash. */
|
|
Packit |
0021fb |
if (should_rehash && dict->size > 0.3 * n(dict))
|
|
Packit |
0021fb |
goto rehash;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
memmove(getkey(dict, slot_n), key, dict->keys.elt_size);
|
|
Packit |
0021fb |
memmove(getvalue(dict, slot_n), value, dict->values.elt_size);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
bitp(dict, slot_n)->taken = 1;
|
|
Packit |
0021fb |
bitp(dict, slot_n)->erased = 0;
|
|
Packit |
0021fb |
++dict->size;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void *
|
|
Packit |
0021fb |
dict_find(struct dict *dict, const void *key)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (dict->size == 0)
|
|
Packit |
0021fb |
return NULL;
|
|
Packit |
0021fb |
assert(n(dict) > 0);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int found;
|
|
Packit |
0021fb |
size_t slot_n = find_slot(dict, key, &found, NULL, NULL);
|
|
Packit |
0021fb |
if (found)
|
|
Packit |
0021fb |
return getvalue(dict, slot_n);
|
|
Packit |
0021fb |
else
|
|
Packit |
0021fb |
return NULL;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_erase(struct dict *dict, const void *key,
|
|
Packit |
0021fb |
void (*dtor_key)(void *tgt, void *data),
|
|
Packit |
0021fb |
void (*dtor_value)(void *tgt, void *data),
|
|
Packit |
0021fb |
void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
int found;
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
size_t slot_n = find_slot(dict, key, &found, NULL, &i);
|
|
Packit |
0021fb |
if (!found)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (dtor_key != NULL)
|
|
Packit |
0021fb |
dtor_key(getkey(dict, slot_n), data);
|
|
Packit |
0021fb |
if (dtor_value != NULL)
|
|
Packit |
0021fb |
dtor_value(getvalue(dict, slot_n), data);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
bitp(dict, slot_n)->taken = 0;
|
|
Packit |
0021fb |
bitp(dict, slot_n)->erased = 1;
|
|
Packit |
0021fb |
--dict->size;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (dict->size < 0.3 * n(dict)) {
|
|
Packit |
0021fb |
size_t smaller = smaller_size(n(dict));
|
|
Packit |
0021fb |
if (smaller != n(dict))
|
|
Packit |
0021fb |
/* Don't mind if it fails when shrinking. */
|
|
Packit |
0021fb |
rehash(dict, smaller);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void *
|
|
Packit |
0021fb |
dict_each(struct dict *dict, void *start_after,
|
|
Packit |
0021fb |
enum callback_status (*cb)(void *, void *, void *), void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
if (start_after != NULL)
|
|
Packit |
0021fb |
i = ((start_after - dict->keys.data) / dict->keys.elt_size) + 1;
|
|
Packit |
0021fb |
else
|
|
Packit |
0021fb |
i = 0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
for (; i < dict->keys.size; ++i)
|
|
Packit |
0021fb |
if (bitp(dict, i)->taken && !bitp(dict, i)->erased) {
|
|
Packit |
0021fb |
void *key = getkey(dict, i);
|
|
Packit |
0021fb |
if (cb(key, getvalue(dict, i), data) != CBS_CONT)
|
|
Packit |
0021fb |
return key;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return NULL;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
size_t
|
|
Packit |
0021fb |
dict_hash_int(const int *key)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return (size_t)(*key * 2654435761U);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_eq_int(const int *key1, const int *key2)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return *key1 == *key2;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
size_t
|
|
Packit |
0021fb |
dict_hash_string(const char **key)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
size_t h = 5381;
|
|
Packit |
0021fb |
const char *str = *key;
|
|
Packit |
0021fb |
while (*str != 0)
|
|
Packit |
0021fb |
h = h * 33 ^ *str++;
|
|
Packit |
0021fb |
return h;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_eq_string(const char **key1, const char **key2)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return strcmp(*key1, *key2) == 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void
|
|
Packit |
0021fb |
dict_dtor_string(const char **key, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
free((char *)*key);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
dict_clone_string(const char **tgt, const char **src, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
*tgt = strdup(*src);
|
|
Packit |
0021fb |
return *tgt != NULL ? 0 : -1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#ifdef TEST
|
|
Packit |
0021fb |
static enum callback_status
|
|
Packit |
0021fb |
dump(int *key, int *value, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
char *seen = data;
|
|
Packit |
0021fb |
assert(seen[*key] == 0);
|
|
Packit |
0021fb |
seen[*key] = 1;
|
|
Packit |
0021fb |
assert(*value == *key * 2 + 1);
|
|
Packit |
0021fb |
return CBS_STOP;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static size_t
|
|
Packit |
0021fb |
dict_hash_int_silly(const int *key)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
return *key % 10;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
verify(struct dict *di, size_t len, char *seen)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
size_t ct = 0;
|
|
Packit |
0021fb |
int *it;
|
|
Packit |
0021fb |
for (it = NULL; (it = DICT_EACH(di, int, int, it, dump, seen)) != NULL;)
|
|
Packit |
0021fb |
ct++;
|
|
Packit |
0021fb |
assert(ct == len);
|
|
Packit |
0021fb |
memset(seen, 0, len);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static enum callback_status
|
|
Packit |
0021fb |
fill_keys(int *key, int *value, void *data)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
int *array = data;
|
|
Packit |
0021fb |
array[++array[0]] = *key;
|
|
Packit |
0021fb |
return CBS_CONT;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
test1(void)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
struct dict di;
|
|
Packit |
0021fb |
DICT_INIT(&di, int, int, dict_hash_int, dict_eq_int, NULL);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
char seen[100000] = {};
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
for (i = 0; i < sizeof(seen); ++i) {
|
|
Packit |
0021fb |
int key = i;
|
|
Packit |
0021fb |
int value = 2 * i + 1;
|
|
Packit |
0021fb |
DICT_INSERT(&di, &key, &value);
|
|
Packit |
0021fb |
int *valp = DICT_FIND_REF(&di, &key, int);
|
|
Packit |
0021fb |
assert(valp != NULL);
|
|
Packit |
0021fb |
assert(*valp == value);
|
|
Packit |
0021fb |
assert(dict_size(&di) == i + 1);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
verify(&di, sizeof(seen), seen);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct dict d2;
|
|
Packit |
0021fb |
DICT_CLONE(&d2, &di, int, int, NULL, NULL, NULL, NULL, NULL);
|
|
Packit |
0021fb |
DICT_DESTROY(&di, int, int, NULL, NULL, NULL);
|
|
Packit |
0021fb |
verify(&d2, sizeof(seen), seen);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* Now we try to gradually erase all elements. We can't erase
|
|
Packit |
0021fb |
* inside a DICT_EACH call, so copy first keys to a separate
|
|
Packit |
0021fb |
* memory area first. */
|
|
Packit |
0021fb |
int keys[d2.size + 1];
|
|
Packit |
0021fb |
size_t ct = 0;
|
|
Packit |
0021fb |
keys[0] = 0;
|
|
Packit |
0021fb |
DICT_EACH(&d2, int, int, NULL, fill_keys, keys);
|
|
Packit |
0021fb |
for (i = 0; i < (size_t)keys[0]; ++i) {
|
|
Packit |
0021fb |
assert(DICT_ERASE(&d2, &keys[i + 1], int,
|
|
Packit |
0021fb |
NULL, NULL, NULL) == 0);
|
|
Packit |
0021fb |
++ct;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
assert(ct == sizeof(seen));
|
|
Packit |
0021fb |
DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
test_erase(void)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
int i;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* To test erase, we need a relatively bad hash function, so
|
|
Packit |
0021fb |
* that there are some overlapping chains in the table. */
|
|
Packit |
0021fb |
struct dict d2;
|
|
Packit |
0021fb |
DICT_INIT(&d2, int, int, dict_hash_int_silly, dict_eq_int, NULL);
|
|
Packit |
0021fb |
const int limit = 500;
|
|
Packit |
0021fb |
for (i = 0; i < limit; ++i) {
|
|
Packit |
0021fb |
int key = 2 * i + 1;
|
|
Packit |
0021fb |
int value = 2 * key + 1;
|
|
Packit |
0021fb |
DICT_INSERT(&d2, &key, &value);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* Now we try to delete each of the keys, and verify that none
|
|
Packit |
0021fb |
* of the chains was broken. */
|
|
Packit |
0021fb |
for (i = 0; i < limit; ++i) {
|
|
Packit |
0021fb |
struct dict copy;
|
|
Packit |
0021fb |
DICT_CLONE(©, &d2, int, int, NULL, NULL, NULL, NULL, NULL);
|
|
Packit |
0021fb |
int key = 2 * i + 1;
|
|
Packit |
0021fb |
DICT_ERASE(©, &key, int, NULL, NULL, NULL);
|
|
Packit |
0021fb |
assert(dict_size(©) == dict_size(&d2) - 1);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int j;
|
|
Packit |
0021fb |
for (j = 0; j < limit; ++j) {
|
|
Packit |
0021fb |
key = 2 * j + 1;
|
|
Packit |
0021fb |
int *valp = DICT_FIND_REF(©, &key, int);
|
|
Packit |
0021fb |
if (i != j) {
|
|
Packit |
0021fb |
assert(valp != NULL);
|
|
Packit |
0021fb |
assert(*valp == 2 * key + 1);
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
assert(valp == NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
DICT_DESTROY(©, int, int, NULL, NULL, NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int main(int argc, char *argv[])
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
test1();
|
|
Packit |
0021fb |
test_erase();
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#endif
|