Remove HASHTABLE
Not used anyware, should have been removed ages ago.
This commit is contained in:
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018 MariaDB Corporation Ab
|
|
||||||
*
|
|
||||||
* Use of this software is governed by the Business Source License included
|
|
||||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
||||||
*
|
|
||||||
* Change Date: 2022-01-01
|
|
||||||
*
|
|
||||||
* On the date above, in accordance with the Business Source License, use
|
|
||||||
* of this software will be governed by version 2 or later of the General
|
|
||||||
* Public License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file hashtable.h A general purpose hashtable mechanism for use within the
|
|
||||||
* gateway
|
|
||||||
*/
|
|
||||||
#include <maxscale/cdefs.h>
|
|
||||||
#include <maxscale/debug.h>
|
|
||||||
#include <maxscale/spinlock.h>
|
|
||||||
|
|
||||||
MXS_BEGIN_DECLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The entries within a hashtable.
|
|
||||||
*
|
|
||||||
* A NULL value for key indicates an empty entry.
|
|
||||||
* The next pointer is the overflow chain for this hashentry.
|
|
||||||
*/
|
|
||||||
typedef struct hashentry
|
|
||||||
{
|
|
||||||
void *key; /**< The value of the key or NULL if empty entry */
|
|
||||||
void *value; /**< The value associated with key */
|
|
||||||
struct hashentry *next; /**< The overflow chain */
|
|
||||||
} HASHENTRIES;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HASHTABLE iterator - used to walk the hashtable in a thread safe
|
|
||||||
* way
|
|
||||||
*/
|
|
||||||
typedef struct hashiterator
|
|
||||||
{
|
|
||||||
struct hashtable *table; /**< The hashtable the iterator refers to */
|
|
||||||
int chain; /**< The current chain we are walking */
|
|
||||||
int depth; /**< The current depth down the chain */
|
|
||||||
} HASHITERATOR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type definition for the hash function
|
|
||||||
*/
|
|
||||||
typedef int (*HASHHASHFN)(const void *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type definition for the comparison function
|
|
||||||
*/
|
|
||||||
typedef int (*HASHCMPFN)(const void *, const void *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type definition for the key/value copying functions
|
|
||||||
*/
|
|
||||||
typedef void *(*HASHCOPYFN)(const void *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type definition for the key/value freeing functions
|
|
||||||
*/
|
|
||||||
typedef void (*HASHFREEFN)(void *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The general purpose hashtable struct.
|
|
||||||
*/
|
|
||||||
typedef struct hashtable
|
|
||||||
{
|
|
||||||
int hashsize; /**< The number of HASHENTRIES */
|
|
||||||
HASHENTRIES **entries; /**< The entries themselves */
|
|
||||||
HASHHASHFN hashfn; /**< The hash function */
|
|
||||||
HASHCMPFN cmpfn; /**< The key comparison function */
|
|
||||||
HASHCOPYFN kcopyfn; /**< Optional key copy function */
|
|
||||||
HASHCOPYFN vcopyfn; /**< Optional value copy function */
|
|
||||||
HASHFREEFN kfreefn; /**< Optional key free function */
|
|
||||||
HASHFREEFN vfreefn; /**< Optional value free function */
|
|
||||||
SPINLOCK spin; /**< Internal spinlock for the hashtable */
|
|
||||||
int n_readers; /**< Number of clients reading the table */
|
|
||||||
int writelock; /**< The table is locked by a writer */
|
|
||||||
bool ht_isflat; /**< Indicates whether hashtable is in stack or heap */
|
|
||||||
int n_elements; /**< Number of added elements */
|
|
||||||
} HASHTABLE;
|
|
||||||
|
|
||||||
extern HASHTABLE *hashtable_alloc(int, HASHHASHFN hashfn, HASHCMPFN cmpfn);
|
|
||||||
HASHTABLE *hashtable_alloc_flat(HASHTABLE* target,
|
|
||||||
int size,
|
|
||||||
HASHHASHFN hashfn,
|
|
||||||
HASHCMPFN cmpfn);
|
|
||||||
/**< Allocate a hashtable */
|
|
||||||
extern void hashtable_memory_fns(HASHTABLE *table,
|
|
||||||
HASHCOPYFN kcopyfn,
|
|
||||||
HASHCOPYFN vcopyfn,
|
|
||||||
HASHFREEFN kfreefn,
|
|
||||||
HASHFREEFN vfreefn);
|
|
||||||
/**< Provide an interface to control key/value memory
|
|
||||||
* manipulation
|
|
||||||
*/
|
|
||||||
extern void hashtable_free(HASHTABLE *); /**< Free a hashtable */
|
|
||||||
extern int hashtable_add(HASHTABLE *, void *, void *); /**< Add an entry */
|
|
||||||
extern int hashtable_delete(HASHTABLE *, void *);
|
|
||||||
/**< Delete an entry table */
|
|
||||||
extern void *hashtable_fetch(HASHTABLE *, void *);
|
|
||||||
/**< Fetch the data for a given key */
|
|
||||||
extern void hashtable_stats(HASHTABLE *); /**< Print statisitics */
|
|
||||||
void hashtable_get_stats(void* hashtable,
|
|
||||||
int* hashsize,
|
|
||||||
int* nelems,
|
|
||||||
int* longest);
|
|
||||||
extern int hashtable_save(HASHTABLE *,
|
|
||||||
const char *filename,
|
|
||||||
int (*keywrite)(int, void*),
|
|
||||||
int (*valuewrite)(int, void*));
|
|
||||||
extern int hashtable_load(HASHTABLE *,
|
|
||||||
const char *filename,
|
|
||||||
void *(*keyread)(int),
|
|
||||||
void *(*valueread)(int));
|
|
||||||
|
|
||||||
extern HASHITERATOR *hashtable_iterator(HASHTABLE *);
|
|
||||||
/**< Allocate an iterator on the hashtable */
|
|
||||||
extern void *hashtable_next(HASHITERATOR *);
|
|
||||||
/**< Return the key of the hash table iterator */
|
|
||||||
extern void hashtable_iterator_free(HASHITERATOR *);
|
|
||||||
extern int hashtable_size(HASHTABLE *table);
|
|
||||||
|
|
||||||
extern void hashtable_item_free(void *data);
|
|
||||||
extern int hashtable_item_strcasecmp(const void* str1, const void* str2);
|
|
||||||
extern int hashtable_item_strcmp(const void* str1, const void* str2);
|
|
||||||
extern void* hashtable_item_strdup(const void *str);
|
|
||||||
extern int hashtable_item_strhash(const void *str);
|
|
||||||
|
|
||||||
MXS_END_DECLS
|
|
||||||
@ -12,7 +12,6 @@ add_library(maxscale-common SHARED
|
|||||||
event.cc
|
event.cc
|
||||||
externcmd.cc
|
externcmd.cc
|
||||||
filter.cc
|
filter.cc
|
||||||
hashtable.cc
|
|
||||||
hint.cc
|
hint.cc
|
||||||
housekeeper.cc
|
housekeeper.cc
|
||||||
httprequest.cc
|
httprequest.cc
|
||||||
|
|||||||
@ -1,921 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
||||||
*
|
|
||||||
* Use of this software is governed by the Business Source License included
|
|
||||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
||||||
*
|
|
||||||
* Change Date: 2022-01-01
|
|
||||||
*
|
|
||||||
* On the date above, in accordance with the Business Source License, use
|
|
||||||
* of this software will be governed by version 2 or later of the General
|
|
||||||
* Public License.
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <maxscale/alloc.h>
|
|
||||||
#include <maxbase/atomic.h>
|
|
||||||
#include <maxscale/hashtable.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file hashtable.c General purpose hashtable routines
|
|
||||||
*
|
|
||||||
* The hashtable can be create with a custom number of hash buckets,
|
|
||||||
* a hash function and optional functions to call make copies of the key
|
|
||||||
* and value and to free them.
|
|
||||||
*
|
|
||||||
* The hashtable is arrange as a set of linked lists, the number of linked
|
|
||||||
* lists being the hashsize as requested by the user. Entries are hashed by
|
|
||||||
* calling the hash function that is passed in by the user, this is used as
|
|
||||||
* an index into the array of linked lists, usign modulo hashsize.
|
|
||||||
*
|
|
||||||
* The linked lists are searched using the key comparison function that is
|
|
||||||
* passed into the hash table creation routine.
|
|
||||||
*
|
|
||||||
* By default the hash table keeps the original pointers that are passed in
|
|
||||||
* for the keys and values, however two functions can be supplied to copy these
|
|
||||||
* a copy function and a free function. Please note the same function is used for
|
|
||||||
* the key and the value, if the actions required are different the called functions
|
|
||||||
* must understand how to differenate the key and value.
|
|
||||||
*
|
|
||||||
* The hash table implements a single write, multiple reader locking policy by
|
|
||||||
* using a pair of counters and a spinlock. The spinlock is used to protect the
|
|
||||||
* number of readers and writers counters when taking out locks. Releasing of
|
|
||||||
* locks uses pure atomic actions and thus does not require spinlock protection.
|
|
||||||
*
|
|
||||||
* @verbatim
|
|
||||||
* Revision History
|
|
||||||
*
|
|
||||||
* Date Who Description
|
|
||||||
* 23/06/2013 Mark Riddoch Initial implementation
|
|
||||||
* 23/07/2013 Mark Riddoch Addition of hashtable iterator
|
|
||||||
* 08/01/2014 Massimiliano Pinto Added copy and free funtion pointers for keys and values:
|
|
||||||
* it's possible to copy and free different data types via
|
|
||||||
* kcopyfn/kfreefn, vcopyfn/vfreefn
|
|
||||||
* 06/02/2015 Mark Riddoch Addition of hashtable_save and hashtable_load
|
|
||||||
*
|
|
||||||
* @endverbatim
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void hashtable_read_lock(HASHTABLE *table);
|
|
||||||
static void hashtable_read_unlock(HASHTABLE *table);
|
|
||||||
static void hashtable_write_lock(HASHTABLE *table);
|
|
||||||
static void hashtable_write_unlock(HASHTABLE *table);
|
|
||||||
static HASHTABLE *hashtable_alloc_real(HASHTABLE* target,
|
|
||||||
int size,
|
|
||||||
HASHHASHFN hashfn,
|
|
||||||
HASHCMPFN cmpfn);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special identity function used as default key/value copy function in the hashtable
|
|
||||||
* implementation. This avoids having to special case the code that manipulates
|
|
||||||
* the keys and values
|
|
||||||
*
|
|
||||||
* @param data The data pointer
|
|
||||||
* @return Return the value we were called with
|
|
||||||
*/
|
|
||||||
static void *
|
|
||||||
identityfn(const void *data)
|
|
||||||
{
|
|
||||||
return (void*)data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special null function used as default free function in the hashtable
|
|
||||||
* implementation. This avoids having to special case the code that manipulates
|
|
||||||
* the keys and values
|
|
||||||
*
|
|
||||||
* @param data The data pointer
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
nullfn(void *data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new hash table.
|
|
||||||
*
|
|
||||||
* The hashtable must have a size of at least one, however to be of any
|
|
||||||
* practical use a larger size sould be chosen as the size relates to the number
|
|
||||||
* of has buckets in the table.
|
|
||||||
*
|
|
||||||
* @param size The size of the hash table, msut be > 0
|
|
||||||
* @param hashfn The user supplied hash function
|
|
||||||
* @param cmpfn The user supplied key comparison function
|
|
||||||
* @return The hashtable table
|
|
||||||
*/
|
|
||||||
HASHTABLE *
|
|
||||||
hashtable_alloc(int size, HASHHASHFN hashfn, HASHCMPFN cmpfn)
|
|
||||||
{
|
|
||||||
return hashtable_alloc_real(NULL, size, hashfn, cmpfn);
|
|
||||||
}
|
|
||||||
|
|
||||||
HASHTABLE* hashtable_alloc_flat(HASHTABLE* target,
|
|
||||||
int size,
|
|
||||||
HASHHASHFN hashfn,
|
|
||||||
HASHCMPFN cmpfn)
|
|
||||||
{
|
|
||||||
return hashtable_alloc_real(target, size, hashfn, cmpfn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static HASHTABLE *
|
|
||||||
hashtable_alloc_real(HASHTABLE* target,
|
|
||||||
int size,
|
|
||||||
HASHHASHFN hashfn,
|
|
||||||
HASHCMPFN cmpfn)
|
|
||||||
{
|
|
||||||
HASHTABLE *rval;
|
|
||||||
|
|
||||||
if (target == NULL)
|
|
||||||
{
|
|
||||||
if ((rval = (HASHTABLE*)MXS_MALLOC(sizeof(HASHTABLE))) == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
rval->ht_isflat = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rval = target;
|
|
||||||
rval->ht_isflat = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
rval->hashsize = size > 0 ? size : 1;
|
|
||||||
rval->hashfn = hashfn;
|
|
||||||
rval->cmpfn = cmpfn;
|
|
||||||
rval->kcopyfn = identityfn;
|
|
||||||
rval->vcopyfn = identityfn;
|
|
||||||
rval->kfreefn = nullfn;
|
|
||||||
rval->vfreefn = nullfn;
|
|
||||||
rval->n_readers = 0;
|
|
||||||
rval->writelock = 0;
|
|
||||||
rval->n_elements = 0;
|
|
||||||
spinlock_init(&rval->spin);
|
|
||||||
if ((rval->entries = (HASHENTRIES **)MXS_CALLOC(rval->hashsize, sizeof(HASHENTRIES *))) == NULL)
|
|
||||||
{
|
|
||||||
MXS_FREE(rval);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(rval->entries, 0, rval->hashsize * sizeof(HASHENTRIES *));
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an entire hash table
|
|
||||||
*
|
|
||||||
* @param table The hash table to delete
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
hashtable_free(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
HASHENTRIES *entry, *ptr;
|
|
||||||
|
|
||||||
if (table == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashtable_write_lock(table);
|
|
||||||
for (i = 0; i < table->hashsize; i++)
|
|
||||||
{
|
|
||||||
entry = table->entries[i];
|
|
||||||
while (entry)
|
|
||||||
{
|
|
||||||
ptr = entry->next;
|
|
||||||
table->kfreefn(entry->key);
|
|
||||||
table->vfreefn(entry->value);
|
|
||||||
MXS_FREE(entry);
|
|
||||||
entry = ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MXS_FREE(table->entries);
|
|
||||||
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
if (!table->ht_isflat)
|
|
||||||
{
|
|
||||||
MXS_FREE(table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide memory management functions to the hash table. This allows
|
|
||||||
* function pointers to be registered that can make copies of the
|
|
||||||
* key and value and free them as well.
|
|
||||||
*
|
|
||||||
* @param table The hash table
|
|
||||||
* @param kcopyfn The copy function for the key
|
|
||||||
* @param vcopyfn The copy function for the value
|
|
||||||
* @param kfreefn The free function for the key
|
|
||||||
* @param vfreefn The free function for the value
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
hashtable_memory_fns(HASHTABLE *table,
|
|
||||||
HASHCOPYFN kcopyfn,
|
|
||||||
HASHCOPYFN vcopyfn,
|
|
||||||
HASHFREEFN kfreefn,
|
|
||||||
HASHFREEFN vfreefn)
|
|
||||||
{
|
|
||||||
if (kcopyfn != NULL)
|
|
||||||
{
|
|
||||||
table->kcopyfn = kcopyfn;
|
|
||||||
}
|
|
||||||
if (vcopyfn != NULL)
|
|
||||||
{
|
|
||||||
table->vcopyfn = vcopyfn;
|
|
||||||
}
|
|
||||||
if (kfreefn != NULL)
|
|
||||||
{
|
|
||||||
table->kfreefn = kfreefn;
|
|
||||||
}
|
|
||||||
if (vfreefn != NULL)
|
|
||||||
{
|
|
||||||
table->vfreefn = vfreefn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an item to the hash table.
|
|
||||||
*
|
|
||||||
* @param table The hash table to which to add the item
|
|
||||||
* @param key The key of the item
|
|
||||||
* @param value The value for the item
|
|
||||||
* @return Return the number of items added
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
hashtable_add(HASHTABLE *table, void *key, void *value)
|
|
||||||
{
|
|
||||||
unsigned int hashkey;
|
|
||||||
HASHENTRIES *entry;
|
|
||||||
|
|
||||||
if (table == NULL || key == NULL || value == NULL)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table->hashsize <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hashkey = table->hashfn(key) % table->hashsize;
|
|
||||||
}
|
|
||||||
hashtable_write_lock(table);
|
|
||||||
entry = table->entries[hashkey % table->hashsize];
|
|
||||||
while (entry && table->cmpfn(key, entry->key) != 0)
|
|
||||||
{
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
if (entry && table->cmpfn(key, entry->key) == 0)
|
|
||||||
{
|
|
||||||
/* Duplicate key value */
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
HASHENTRIES *ptr = (HASHENTRIES *)MXS_MALLOC(sizeof(HASHENTRIES));
|
|
||||||
if (ptr == NULL)
|
|
||||||
{
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy the key */
|
|
||||||
ptr->key = table->kcopyfn(key);
|
|
||||||
|
|
||||||
/* check succesfull key copy */
|
|
||||||
if (ptr->key == NULL)
|
|
||||||
{
|
|
||||||
MXS_FREE(ptr);
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy the value */
|
|
||||||
ptr->value = table->vcopyfn(value);
|
|
||||||
|
|
||||||
/* check succesfull value copy */
|
|
||||||
if (ptr->value == NULL)
|
|
||||||
{
|
|
||||||
/* remove the key ! */
|
|
||||||
table->kfreefn(ptr->key);
|
|
||||||
MXS_FREE(ptr);
|
|
||||||
|
|
||||||
/* value not copied, return */
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr->next = table->entries[hashkey % table->hashsize];
|
|
||||||
table->entries[hashkey % table->hashsize] = ptr;
|
|
||||||
}
|
|
||||||
table->n_elements++;
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an item from the hash table that has a given key
|
|
||||||
*
|
|
||||||
* @param table The hash table to delete from
|
|
||||||
* @param key The key value of the item to remove
|
|
||||||
* @return Return the number of items deleted
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
hashtable_delete(HASHTABLE *table, void *key)
|
|
||||||
{
|
|
||||||
unsigned int hashkey;
|
|
||||||
HASHENTRIES *entry, *ptr;
|
|
||||||
|
|
||||||
if (table == NULL || key == NULL)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashkey = table->hashfn(key) % table->hashsize;
|
|
||||||
hashtable_write_lock(table);
|
|
||||||
entry = table->entries[hashkey % table->hashsize];
|
|
||||||
while (entry && entry->key && table->cmpfn(key, entry->key) != 0)
|
|
||||||
{
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
if (entry == NULL)
|
|
||||||
{
|
|
||||||
/* Not found */
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry == table->entries[hashkey % table->hashsize])
|
|
||||||
{
|
|
||||||
/* We are removing from the first entry */
|
|
||||||
table->entries[hashkey % table->hashsize] = entry->next;
|
|
||||||
table->kfreefn(entry->key);
|
|
||||||
table->vfreefn(entry->value);
|
|
||||||
|
|
||||||
if (entry->next != NULL)
|
|
||||||
{
|
|
||||||
entry->key = entry->next->key;
|
|
||||||
entry->value = entry->next->value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
entry->key = NULL;
|
|
||||||
entry->value = NULL;
|
|
||||||
}
|
|
||||||
MXS_FREE(entry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ptr = table->entries[hashkey % table->hashsize];
|
|
||||||
while (ptr && ptr->next != entry)
|
|
||||||
{
|
|
||||||
ptr = ptr->next;
|
|
||||||
}
|
|
||||||
if (ptr == NULL)
|
|
||||||
{
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
return 0; /* This should never happen */
|
|
||||||
}
|
|
||||||
ptr->next = entry->next;
|
|
||||||
table->kfreefn(entry->key);
|
|
||||||
table->vfreefn(entry->value);
|
|
||||||
MXS_FREE(entry);
|
|
||||||
}
|
|
||||||
table->n_elements--;
|
|
||||||
assert(table->n_elements >= 0);
|
|
||||||
hashtable_write_unlock(table);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch an item with a given key value from the hash table
|
|
||||||
*
|
|
||||||
* @param table The hash table
|
|
||||||
* @param key The key value
|
|
||||||
* @return The item or NULL if the item was not found
|
|
||||||
*/
|
|
||||||
void *
|
|
||||||
hashtable_fetch(HASHTABLE *table, void *key)
|
|
||||||
{
|
|
||||||
unsigned int hashkey;
|
|
||||||
HASHENTRIES *entry;
|
|
||||||
|
|
||||||
if (table == NULL || key == NULL || 0 == table->hashsize)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashkey = table->hashfn(key) % table->hashsize;
|
|
||||||
hashtable_read_lock(table);
|
|
||||||
entry = table->entries[hashkey % table->hashsize];
|
|
||||||
while (entry && entry->key && table->cmpfn(key, entry->key) != 0)
|
|
||||||
{
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
if (entry == NULL)
|
|
||||||
{
|
|
||||||
hashtable_read_unlock(table);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hashtable_read_unlock(table);
|
|
||||||
return entry->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print hash table statistics to the standard output
|
|
||||||
*
|
|
||||||
* @param table The hash table
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
hashtable_stats(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
int total, longest, i, j;
|
|
||||||
HASHENTRIES *entries;
|
|
||||||
|
|
||||||
if (table == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Hashtable: %p, size %d\n", table, table->hashsize);
|
|
||||||
total = 0;
|
|
||||||
longest = 0;
|
|
||||||
hashtable_read_lock(table);
|
|
||||||
for (i = 0; i < table->hashsize; i++)
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
entries = table->entries[i];
|
|
||||||
while (entries)
|
|
||||||
{
|
|
||||||
j++;
|
|
||||||
entries = entries->next;
|
|
||||||
}
|
|
||||||
total += j;
|
|
||||||
if (j > longest)
|
|
||||||
{
|
|
||||||
longest = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hashtable_read_unlock(table);
|
|
||||||
printf("\tNo. of entries: %d\n", total);
|
|
||||||
printf("\tAverage chain length: %.1f\n", (float)total / table->hashsize);
|
|
||||||
printf("\tLongest chain length: %d\n", longest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces stat output about hashtable
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* @param table - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @param hashsize - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @param nelems - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @param longest - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void hashtable_get_stats(void* table,
|
|
||||||
int* hashsize,
|
|
||||||
int* nelems,
|
|
||||||
int* longest)
|
|
||||||
{
|
|
||||||
HASHTABLE* ht;
|
|
||||||
HASHENTRIES* entries;
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
*nelems = 0;
|
|
||||||
*longest = 0;
|
|
||||||
*hashsize = 0;
|
|
||||||
|
|
||||||
if (table != NULL)
|
|
||||||
{
|
|
||||||
ht = (HASHTABLE *)table;
|
|
||||||
hashtable_read_lock(ht);
|
|
||||||
|
|
||||||
for (i = 0; i < ht->hashsize; i++)
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
entries = ht->entries[i];
|
|
||||||
while (entries)
|
|
||||||
{
|
|
||||||
j++;
|
|
||||||
entries = entries->next;
|
|
||||||
}
|
|
||||||
*nelems += j;
|
|
||||||
if (j > *longest)
|
|
||||||
{
|
|
||||||
*longest = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*hashsize = ht->hashsize;
|
|
||||||
hashtable_read_unlock(ht);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a read lock on the hashtable.
|
|
||||||
*
|
|
||||||
* The hashtable support multiple readers and a single writer,
|
|
||||||
* we have a spinlock to protect the two counts, n_readers and
|
|
||||||
* writelock.
|
|
||||||
*
|
|
||||||
* We take the hashtable spinlock and then check that writelock
|
|
||||||
* is set to zero. If not we release the spinlock and do dirty
|
|
||||||
* reads of writelock until it goes to 0. Once it is zero we
|
|
||||||
* acquire the spinlock again and test that writelock is still
|
|
||||||
* 0.
|
|
||||||
*
|
|
||||||
* With writelock set to zero we increment n_readers with the
|
|
||||||
* spinlock still held.
|
|
||||||
*
|
|
||||||
* @param table The hashtable to lock.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
hashtable_read_lock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
while (table->writelock)
|
|
||||||
{
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
while (atomic_add(&table->writelock, 1) != 0)
|
|
||||||
{
|
|
||||||
atomic_add(&table->writelock, -1);
|
|
||||||
}
|
|
||||||
atomic_add(&table->writelock, -1);
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
}
|
|
||||||
atomic_add(&table->n_readers, 1);
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release a previously obtained readlock.
|
|
||||||
*
|
|
||||||
* Simply decrement the n_readers value for the hash table
|
|
||||||
*
|
|
||||||
* @param table The hash table to unlock
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
hashtable_read_unlock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
atomic_add(&table->n_readers, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain an exclusive write lock for the hash table.
|
|
||||||
*
|
|
||||||
* We acquire the hashtable spinlock, check for the number of
|
|
||||||
* readers beign zero. If it is not we hold the spinlock and
|
|
||||||
* loop waiting for the n_readers to reach zero. This will prevent
|
|
||||||
* any new readers beign granted access but will not prevent current
|
|
||||||
* readers releasing the read lock.
|
|
||||||
*
|
|
||||||
* Once we have no readers we increment writelock and test if we are
|
|
||||||
* the only writelock holder, if not we repeat the process. We hold
|
|
||||||
* the spinlock throughout the process since both read and write
|
|
||||||
* locks do not require the spinlock to be acquired.
|
|
||||||
*
|
|
||||||
* @param table The table to lock for updates
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
hashtable_write_lock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
int available;
|
|
||||||
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
while (atomic_add(&table->n_readers, 1) != 0)
|
|
||||||
{
|
|
||||||
atomic_add(&table->n_readers, -1);
|
|
||||||
}
|
|
||||||
atomic_add(&table->n_readers, -1);
|
|
||||||
available = atomic_add(&table->writelock, 1);
|
|
||||||
if (available != 0)
|
|
||||||
{
|
|
||||||
atomic_add(&table->writelock, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (available != 0);
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the write lock on the hash table.
|
|
||||||
*
|
|
||||||
* @param table The hash table to unlock
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
hashtable_write_unlock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
atomic_add(&table->writelock, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an iterator on a hash table
|
|
||||||
*
|
|
||||||
* @param table The table to ceate an iterator on
|
|
||||||
* @return An iterator to use in future calls
|
|
||||||
*/
|
|
||||||
HASHITERATOR *
|
|
||||||
hashtable_iterator(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
HASHITERATOR *rval = (HASHITERATOR *)MXS_MALLOC(sizeof(HASHITERATOR));
|
|
||||||
|
|
||||||
if (rval)
|
|
||||||
{
|
|
||||||
rval->table = table;
|
|
||||||
rval->chain = 0;
|
|
||||||
rval->depth = -1;
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next key for a hashtable iterator
|
|
||||||
*
|
|
||||||
* @param iter The hashtable iterator
|
|
||||||
* @return The next key value or NULL
|
|
||||||
*/
|
|
||||||
void *
|
|
||||||
hashtable_next(HASHITERATOR *iter)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
HASHENTRIES *entries;
|
|
||||||
|
|
||||||
if (iter == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
iter->depth++;
|
|
||||||
while (iter->chain < iter->table->hashsize)
|
|
||||||
{
|
|
||||||
hashtable_read_lock(iter->table);
|
|
||||||
if ((entries = iter->table->entries[iter->chain]) != NULL)
|
|
||||||
{
|
|
||||||
i = 0;
|
|
||||||
while (entries && i < iter->depth)
|
|
||||||
{
|
|
||||||
entries = entries->next;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
hashtable_read_unlock(iter->table);
|
|
||||||
if (entries)
|
|
||||||
{
|
|
||||||
return entries->key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hashtable_read_unlock(iter->table);
|
|
||||||
}
|
|
||||||
iter->depth = 0;
|
|
||||||
iter->chain++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a hashtable iterator
|
|
||||||
*
|
|
||||||
* @param iter The iterator to free
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
hashtable_iterator_free(HASHITERATOR *iter)
|
|
||||||
{
|
|
||||||
MXS_FREE(iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save a hashtable to disk
|
|
||||||
*
|
|
||||||
* @param table Hashtable to save
|
|
||||||
* @param filename Filename to write hashtable into
|
|
||||||
* @param keywrite Pointer to function that writes a single key
|
|
||||||
* @param valuewrite Pointer to function that writes a single value
|
|
||||||
* @return Number of entries written or -1 on error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
hashtable_save(HASHTABLE *table, const char *filename,
|
|
||||||
int (*keywrite)(int, void*),
|
|
||||||
int (*valuewrite)(int, void*))
|
|
||||||
{
|
|
||||||
int fd, rval = 0;
|
|
||||||
HASHITERATOR *iter;
|
|
||||||
void *key, *value;
|
|
||||||
|
|
||||||
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (write(fd, "HASHTABLE", 7) != 7) // Magic number
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (write(fd, &rval, sizeof(rval)) == -1) // Write zero counter, will be overrwriten at end
|
|
||||||
{
|
|
||||||
MXS_ERROR("Failed to write hashtable item count: %d, %s", errno,
|
|
||||||
mxs_strerror(errno));
|
|
||||||
}
|
|
||||||
if ((iter = hashtable_iterator(table)) != NULL)
|
|
||||||
{
|
|
||||||
while ((key = hashtable_next(iter)) != NULL)
|
|
||||||
{
|
|
||||||
if (!(*keywrite)(fd, key))
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
hashtable_iterator_free(iter);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ((value = hashtable_fetch(table, key)) == NULL ||
|
|
||||||
(*valuewrite)(fd, value) == 0)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
hashtable_iterator_free(iter);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
rval++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now go back and write the count of entries */
|
|
||||||
if (lseek(fd, 7L, SEEK_SET) != -1)
|
|
||||||
{
|
|
||||||
if (write(fd, &rval, sizeof(rval)) == -1)
|
|
||||||
{
|
|
||||||
MXS_ERROR("Failed to write hashtable item count: %d, %s", errno,
|
|
||||||
mxs_strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
hashtable_iterator_free(iter);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a hashtable from disk
|
|
||||||
*
|
|
||||||
* @param table Hashtable to load
|
|
||||||
* @param filename Filename to read hashtable from
|
|
||||||
* @param keyread Pointer to function that reads a single key
|
|
||||||
* @param valueread Pointer to function that reads a single value
|
|
||||||
* @return Number of entries read or -1 on error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
hashtable_load(HASHTABLE *table, const char *filename,
|
|
||||||
void *(*keyread)(int),
|
|
||||||
void *(*valueread)(int))
|
|
||||||
{
|
|
||||||
int fd, count, rval = 0;
|
|
||||||
void *key, *value;
|
|
||||||
char buf[40];
|
|
||||||
|
|
||||||
if ((fd = open(filename, O_RDONLY)) == -1)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (read(fd, buf, 7) != 7)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (strncmp(buf, "HASHTABLE", 7) != 0)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (read(fd, &count, sizeof(count)) != sizeof(count))
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
key = keyread(fd);
|
|
||||||
value = valueread(fd);
|
|
||||||
if (key == NULL || value == NULL)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hashtable_add(table, key, value);
|
|
||||||
rval++;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of elements added to the hashtable
|
|
||||||
* @param table Hashtable to measure
|
|
||||||
* @return Number of inserted elements or 0 if table is NULL
|
|
||||||
*/
|
|
||||||
int hashtable_size(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
assert(table);
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
int rval = table->n_elements;
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees memory assumed to have been allocated using one of the MaxScale
|
|
||||||
* allocation functions. Intended to be used together with a hashtable
|
|
||||||
* copy function that merely makes a straight memory copy of the key/value,
|
|
||||||
* e.g. hashtable_item_strdup.
|
|
||||||
* @param data The memory to be freed.
|
|
||||||
*/
|
|
||||||
void hashtable_item_free(void *data)
|
|
||||||
{
|
|
||||||
MXS_FREE(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function intended for use as the comparison function of a hashtable,
|
|
||||||
* when the key is a NULL terminated string. Behaves as strcasecmp.
|
|
||||||
* @param str1 Pointer to string.
|
|
||||||
* @param str2 Pointer to string.
|
|
||||||
* @return Same as strcasecmp.
|
|
||||||
*/
|
|
||||||
int hashtable_item_strcasecmp(const void *str1, const void *str2)
|
|
||||||
{
|
|
||||||
return strcasecmp((const char*)str1, (const char*)str2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function intended for use as the comparison function of a hashtable,
|
|
||||||
* when the key is a NULL terminated string. Behaves as strcmp.
|
|
||||||
* @param str1 Pointer to string.
|
|
||||||
* @param str2 Pointer to string.
|
|
||||||
* @return Same as strcmp.
|
|
||||||
*/
|
|
||||||
int hashtable_item_strcmp(const void *str1, const void *str2)
|
|
||||||
{
|
|
||||||
return strcmp((const char*)str1, (const char*)str2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function intended for use as the copy function of a hashtable,
|
|
||||||
* when the key/value is a NULL terminated string.
|
|
||||||
* @param data A pointer to a NULL terminated string.
|
|
||||||
* @return A copy of the provided string or NULL if memory
|
|
||||||
* allocation fails.
|
|
||||||
*/
|
|
||||||
void* hashtable_item_strdup(const void* data)
|
|
||||||
{
|
|
||||||
return MXS_STRDUP((const char*)data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function intended for use as the hash function of a hashtable,
|
|
||||||
* when the key is a NULL terminated string.
|
|
||||||
* @param data A pointer to a NULL terminated string.
|
|
||||||
* @return A hash of the string.
|
|
||||||
*/
|
|
||||||
int hashtable_item_strhash(const void* data)
|
|
||||||
{
|
|
||||||
int hash = 0;
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
const char* key = (const char*)data;
|
|
||||||
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = *key++))
|
|
||||||
{
|
|
||||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
@ -6,7 +6,6 @@ add_executable(test_config test_config.cc)
|
|||||||
add_executable(test_dcb test_dcb.cc)
|
add_executable(test_dcb test_dcb.cc)
|
||||||
add_executable(test_event test_event.cc)
|
add_executable(test_event test_event.cc)
|
||||||
add_executable(test_filter test_filter.cc)
|
add_executable(test_filter test_filter.cc)
|
||||||
add_executable(test_hash test_hash.cc)
|
|
||||||
add_executable(test_hint test_hint.cc)
|
add_executable(test_hint test_hint.cc)
|
||||||
add_executable(test_http test_http.cc)
|
add_executable(test_http test_http.cc)
|
||||||
add_executable(test_json test_json.cc)
|
add_executable(test_json test_json.cc)
|
||||||
@ -35,7 +34,6 @@ target_link_libraries(test_config maxscale-common)
|
|||||||
target_link_libraries(test_dcb maxscale-common)
|
target_link_libraries(test_dcb maxscale-common)
|
||||||
target_link_libraries(test_event maxscale-common)
|
target_link_libraries(test_event maxscale-common)
|
||||||
target_link_libraries(test_filter maxscale-common)
|
target_link_libraries(test_filter maxscale-common)
|
||||||
target_link_libraries(test_hash maxscale-common)
|
|
||||||
target_link_libraries(test_hint maxscale-common)
|
target_link_libraries(test_hint maxscale-common)
|
||||||
target_link_libraries(test_http maxscale-common)
|
target_link_libraries(test_http maxscale-common)
|
||||||
target_link_libraries(test_json maxscale-common)
|
target_link_libraries(test_json maxscale-common)
|
||||||
@ -63,7 +61,6 @@ add_test(test_config test_config)
|
|||||||
add_test(test_dcb test_dcb)
|
add_test(test_dcb test_dcb)
|
||||||
add_test(test_event test_event)
|
add_test(test_event test_event)
|
||||||
add_test(test_filter test_filter)
|
add_test(test_filter test_filter)
|
||||||
add_test(test_hash test_hash)
|
|
||||||
add_test(test_hint test_hint)
|
add_test(test_hint test_hint)
|
||||||
add_test(test_http test_http)
|
add_test(test_http test_http)
|
||||||
add_test(test_json test_json)
|
add_test(test_json test_json)
|
||||||
|
|||||||
@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
||||||
*
|
|
||||||
* Use of this software is governed by the Business Source License included
|
|
||||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
||||||
*
|
|
||||||
* Change Date: 2022-01-01
|
|
||||||
*
|
|
||||||
* On the date above, in accordance with the Business Source License, use
|
|
||||||
* of this software will be governed by version 2 or later of the General
|
|
||||||
* Public License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @verbatim
|
|
||||||
* Revision History
|
|
||||||
*
|
|
||||||
* Date Who Description
|
|
||||||
* 18/08-2014 Mark Riddoch Initial implementation
|
|
||||||
*
|
|
||||||
* @endverbatim
|
|
||||||
*/
|
|
||||||
|
|
||||||
// To ensure that ss_info_assert asserts also when builing in non-debug mode.
|
|
||||||
#if !defined(SS_DEBUG)
|
|
||||||
#define SS_DEBUG
|
|
||||||
#endif
|
|
||||||
#if defined(NDEBUG)
|
|
||||||
#undef NDEBUG
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <maxscale/alloc.h>
|
|
||||||
#include <maxbase/atomic.h>
|
|
||||||
#include <maxscale/hashtable.h>
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_lock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
while (table->writelock)
|
|
||||||
{
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
while (table->writelock)
|
|
||||||
;
|
|
||||||
spinlock_acquire(&table->spin);
|
|
||||||
}
|
|
||||||
table->n_readers++;
|
|
||||||
spinlock_release(&table->spin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_unlock(HASHTABLE *table)
|
|
||||||
{
|
|
||||||
atomic_add(&table->n_readers, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hfun(const void* key);
|
|
||||||
static int cmpfun(const void *, const void *);
|
|
||||||
|
|
||||||
static int hfun(const void* key)
|
|
||||||
{
|
|
||||||
const int *i = (const int *)key;
|
|
||||||
int j = (*i * 23) + 41;
|
|
||||||
return j;
|
|
||||||
/* return *(int *)key; */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmpfun(const void* v1, const void* v2)
|
|
||||||
{
|
|
||||||
int i1;
|
|
||||||
int i2;
|
|
||||||
|
|
||||||
i1 = *(const int *)v1;
|
|
||||||
i2 = *(const int *)v2;
|
|
||||||
|
|
||||||
return (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static double start;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* test1 spinlock_acquire_nowait tests
|
|
||||||
*
|
|
||||||
* Test that spinlock_acquire_nowait returns false if the spinlock
|
|
||||||
* is already taken.
|
|
||||||
*
|
|
||||||
* Test that spinlock_acquire_nowait returns true if the spinlock
|
|
||||||
* is not taken.
|
|
||||||
*
|
|
||||||
* Test that spinlock_acquire_nowait does hold the spinlock.
|
|
||||||
*/
|
|
||||||
static bool do_hashtest(
|
|
||||||
int argelems,
|
|
||||||
int argsize)
|
|
||||||
{
|
|
||||||
bool succp = true;
|
|
||||||
HASHTABLE* h;
|
|
||||||
int nelems;
|
|
||||||
int i;
|
|
||||||
int* val_arr;
|
|
||||||
int hsize;
|
|
||||||
int longest;
|
|
||||||
int* iter;
|
|
||||||
|
|
||||||
fprintf(stderr,
|
|
||||||
"testhash : creating hash table of size %d, including %d "
|
|
||||||
"elements in total, at time %g.",
|
|
||||||
argsize,
|
|
||||||
argelems,
|
|
||||||
(double)clock() - start);
|
|
||||||
|
|
||||||
val_arr = (int *)MXS_MALLOC(sizeof(void *)*argelems);
|
|
||||||
MXS_ABORT_IF_NULL(val_arr);
|
|
||||||
|
|
||||||
h = hashtable_alloc(argsize, hfun, cmpfun);
|
|
||||||
|
|
||||||
fprintf(stderr, "\t..done\nAdd %d elements to hash table.", argelems);
|
|
||||||
|
|
||||||
for (i = 0; i < argelems; i++)
|
|
||||||
{
|
|
||||||
val_arr[i] = i;
|
|
||||||
hashtable_add(h, (void *)&val_arr[i], (void *)&val_arr[i]);
|
|
||||||
}
|
|
||||||
if (argelems > 1000)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\t..done\nOperation took %g", (double)clock() - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\t..done\nRead hash table statistics.");
|
|
||||||
|
|
||||||
hashtable_get_stats((void *)h, &hsize, &nelems, &longest);
|
|
||||||
|
|
||||||
fprintf(stderr, "\t..done\nValidate read values.");
|
|
||||||
|
|
||||||
ss_info_dassert(hsize == (argsize > 0 ? argsize : 1), "Invalid hash size");
|
|
||||||
ss_info_dassert((nelems == argelems) || (nelems == 0 && argsize == 0),
|
|
||||||
"Invalid element count");
|
|
||||||
ss_info_dassert(longest <= nelems, "Too large longest list value");
|
|
||||||
if (argelems > 1000)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\t..done\nOperation took %g", (double)clock() - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\t..done\nValidate iterator.");
|
|
||||||
|
|
||||||
HASHITERATOR *iterator = hashtable_iterator(h);
|
|
||||||
read_lock(h);
|
|
||||||
for (i = 0; i < (argelems + 1); i++)
|
|
||||||
{
|
|
||||||
iter = (int *)hashtable_next(iterator);
|
|
||||||
if (iter == NULL)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (argelems < 100)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\nNext item, iter = %d, i = %d", *iter, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
read_unlock(h);
|
|
||||||
ss_info_dassert((i == argelems) || (i == 0 && argsize == 0), "\nIncorrect number of elements from iterator");
|
|
||||||
hashtable_iterator_free(iterator);
|
|
||||||
if (argelems > 1000)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\t..done\nOperation took %g", (double)clock() - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\t\t..done\n\nTest completed successfully.\n\n");
|
|
||||||
|
|
||||||
hashtable_free(h);
|
|
||||||
|
|
||||||
|
|
||||||
MXS_FREE(val_arr);
|
|
||||||
return succp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @node Simple test which creates hashtable and frees it. Size and number of entries
|
|
||||||
* sre specified by user and passed as arguments.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return 0 if succeed, 1 if failed.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @details (write detailed description here)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
int rc = 1;
|
|
||||||
start = (double) clock();
|
|
||||||
|
|
||||||
if (!do_hashtest(0, 1))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(10, 1))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(1000, 10))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(10, 0))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(10, -5))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(1500, 17))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(1, 1))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(10000, 133))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(1000, 1000))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
if (!do_hashtest(1000, 100000))
|
|
||||||
{
|
|
||||||
goto return_rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
return_rc:
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user