qse/lib/cmn/htl.c

945 lines
21 KiB
C
Raw Normal View History

/*
* $Id$
*
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* hash.c Non-thread-safe split-ordered hash table.
*
* The weird "reverse" function is based on an idea from
* "Split-Ordered Lists - Lock-free Resizable Hash Tables", with
* modifications so that they're not lock-free. :(
*
* However, the split-order idea allows a fast & easy splitting of the
* hash bucket chain when the hash table is resized. Without it, we'd
* have to check & update the pointers for every node in the buck chain,
* rather than being able to move 1/2 of the entries in the chain with
* one update.
*
* Version: $Id$
*
* 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Copyright 2005,2006 The FreeRADIUS server project
*/
#include <qse/cmn/htl.h>
#include <qse/cmn/chr.h>
#include "mem-prv.h"
/*
* A reasonable number of buckets to start off with.
* Should be a power of two.
*/
#define QSE_HTL_NUM_BUCKETS (64)
/*
* perl -e 'foreach $i (0..255) {$r = 0; foreach $j (0 .. 7 ) { if (($i & ( 1<< $j)) != 0) { $r |= (1 << (7 - $j));}} print $r, ", ";if (($i & 7) == 7) {print "\n";}}'
*/
static const qse_uint8_t reversed_byte[256] =
{
0, 128, 64, 192, 32, 160, 96, 224,
16, 144, 80, 208, 48, 176, 112, 240,
8, 136, 72, 200, 40, 168, 104, 232,
24, 152, 88, 216, 56, 184, 120, 248,
4, 132, 68, 196, 36, 164, 100, 228,
20, 148, 84, 212, 52, 180, 116, 244,
12, 140, 76, 204, 44, 172, 108, 236,
28, 156, 92, 220, 60, 188, 124, 252,
2, 130, 66, 194, 34, 162, 98, 226,
18, 146, 82, 210, 50, 178, 114, 242,
10, 138, 74, 202, 42, 170, 106, 234,
26, 154, 90, 218, 58, 186, 122, 250,
6, 134, 70, 198, 38, 166, 102, 230,
22, 150, 86, 214, 54, 182, 118, 246,
14, 142, 78, 206, 46, 174, 110, 238,
30, 158, 94, 222, 62, 190, 126, 254,
1, 129, 65, 193, 33, 161, 97, 225,
17, 145, 81, 209, 49, 177, 113, 241,
9, 137, 73, 201, 41, 169, 105, 233,
25, 153, 89, 217, 57, 185, 121, 249,
5, 133, 69, 197, 37, 165, 101, 229,
21, 149, 85, 213, 53, 181, 117, 245,
13, 141, 77, 205, 45, 173, 109, 237,
29, 157, 93, 221, 61, 189, 125, 253,
3, 131, 67, 195, 35, 163, 99, 227,
19, 147, 83, 211, 51, 179, 115, 243,
11, 139, 75, 203, 43, 171, 107, 235,
27, 155, 91, 219, 59, 187, 123, 251,
7, 135, 71, 199, 39, 167, 103, 231,
23, 151, 87, 215, 55, 183, 119, 247,
15, 143, 79, 207, 47, 175, 111, 239,
31, 159, 95, 223, 63, 191, 127, 255
};
/*
* perl -e 'foreach $i (0..255) {$r = 0;foreach $j (0 .. 7) { $r = $i & (1 << (7 - $j)); last if ($r)} print $i & ~($r), ", ";if (($i & 7) == 7) {print "\n";}}'
*/
static qse_uint8_t parent_byte[256] =
{
0, 0, 0, 1, 0, 1, 2, 3,
0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
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, 26, 27, 28, 29, 30, 31,
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, 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, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
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, 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, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127
};
/*
* Reverse a key.
*/
static qse_uint32_t reverse (qse_uint32_t key)
{
return ((reversed_byte[key & 0xff] << 24) |
(reversed_byte[(key >> 8) & 0xff] << 16) |
(reversed_byte[(key >> 16) & 0xff] << 8) |
(reversed_byte[(key >> 24) & 0xff]));
}
/*
* Take the parent by discarding the highest bit that is set.
*/
static qse_uint32_t parent_of (qse_uint32_t key)
{
if (key > 0x00ffffff)
return (key & 0x00ffffff) | (parent_byte[key >> 24] << 24);
if (key > 0x0000ffff)
return (key & 0x0000ffff) | (parent_byte[key >> 16] << 16);
if (key > 0x000000ff)
return (key & 0x000000ff) | (parent_byte[key >> 8] << 8);
return parent_byte[key];
}
static qse_htl_node_t* __list_find (qse_htl_t* ht, qse_htl_node_t* head, qse_uint32_t reversed, const void* data, qse_htl_comper_t comper)
{
qse_htl_node_t *cur;
if (!/*ht->*/comper) return QSE_NULL;
for (cur = head; cur != &ht->null; cur = cur->next)
{
if (cur->reversed == reversed)
{
int cmp = /*ht->*/comper(ht, data, cur->data);
if (cmp > 0) break;
if (cmp < 0) continue;
return cur;
}
if (cur->reversed > reversed) break;
}
return QSE_NULL;
}
QSE_INLINE static qse_htl_node_t *list_find (qse_htl_t* ht, qse_htl_node_t* head, qse_uint32_t reversed, const void* data)
{
return __list_find (ht, head, reversed, data, ht->comper);
}
/*
* Inserts a new entry into the list, in order.
*/
static int list_insert (qse_htl_t* ht, qse_htl_node_t** head, qse_htl_node_t* node)
{
qse_htl_node_t **last, *cur;
last = head;
for (cur = *head; cur != &ht->null; cur = cur->next)
{
if (cur->reversed > node->reversed) break;
last = &(cur->next);
if (cur->reversed == node->reversed)
{
if (ht->comper)
{
int cmp = ht->comper(ht, node->data, cur->data);
if (cmp > 0) break;
if (cmp < 0) continue;
}
return 0;
}
}
node->next = *last;
*last = node;
return 1;
}
/*
* Delete an entry from the list.
*/
static int list_delete (qse_htl_t* ht, qse_htl_node_t** head, qse_htl_node_t* node)
{
qse_htl_node_t **last, *cur;
last = head;
for (cur = *head; cur != &ht->null; cur = cur->next)
{
if (cur == node) break;
last = &(cur->next);
}
*last = node->next;
return 1;
}
/* ------------------------------------------------------------------------- */
static QSE_INLINE_ALWAYS qse_uint32_t default_hasher (qse_htl_t* htl, const void* data)
{
#if 0
qse_size_t h = 5381;
const qse_byte_t* p = (const qse_byte_t*)data;
const qse_byte_t* bound = p + htl->keysize;
while (p < bound) h = ((h << 5) + h) + *p++;
return h ;
#else
return qse_genhash32 (data, htl->keysize);
#endif
}
static QSE_INLINE_ALWAYS int default_comper (qse_htl_t* htl, const void* data1, const void* data2)
{
return QSE_MEMCMP(data1, data2, htl->keysize);
}
qse_htl_t* qse_htl_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, int keysize)
{
qse_htl_t* htl;
2019-03-05 08:21:01 +00:00
htl = QSE_MMGR_ALLOC(mmgr, QSE_SIZEOF(qse_htl_t) + xtnsize);
if (htl == QSE_NULL) return QSE_NULL;
2019-03-05 08:21:01 +00:00
if (qse_htl_init(htl, mmgr, keysize) <= -1)
{
QSE_MMGR_FREE (mmgr, htl);
return QSE_NULL;
}
QSE_MEMSET (htl + 1, 0, xtnsize);
return htl;
}
void qse_htl_close (qse_htl_t* htl)
{
qse_htl_fini (htl);
QSE_MMGR_FREE (htl->mmgr, htl);
}
/*
* Create the table.
*
* Memory usage in bytes is (20/3) * number of entries.
*/
int qse_htl_init (qse_htl_t* ht, qse_mmgr_t* mmgr, int keysize)
{
QSE_MEMSET (ht, 0, sizeof(*ht));
ht->mmgr = mmgr;
ht->keysize = keysize;
ht->hasher = default_hasher;
ht->comper = default_comper;
ht->freeer = QSE_NULL;
ht->copier = QSE_NULL;
ht->num_buckets = QSE_HTL_NUM_BUCKETS;
ht->mask = ht->num_buckets - 1;
/*
* Have a default load factor of 2.5. In practice this
* means that the average load will hit 3 before the
* table grows.
*/
ht->next_grow = (ht->num_buckets << 1) + (ht->num_buckets >> 1);
ht->buckets = QSE_MMGR_ALLOC (ht->mmgr, QSE_SIZEOF(*ht->buckets) * ht->num_buckets);
if (!ht->buckets) return -1;
QSE_MEMSET (ht->buckets, 0, sizeof(*ht->buckets) * ht->num_buckets);
ht->null.reversed = ~0;
ht->null.key = ~0;
ht->null.next = &ht->null;
ht->buckets[0] = &ht->null;
return 0;
}
void qse_htl_fini (qse_htl_t* ht)
{
qse_htl_clear (ht);
QSE_MMGR_FREE (ht->mmgr, ht->buckets);
}
void qse_htl_clear (qse_htl_t* ht)
{
int i;
qse_htl_node_t* node, * next;
/*
* Walk over the buckets, freeing them all.
*/
for (i = 0; i < ht->num_buckets; i++)
{
if (ht->buckets[i])
{
for (node = ht->buckets[i]; node != &ht->null; node = next)
{
next = node->next;
if (!node->data) continue; /* dummy entry */
if (ht->freeer) ht->freeer (ht, node->data);
QSE_MMGR_FREE (ht->mmgr, node);
}
}
}
QSE_MEMSET (ht->buckets, 0, sizeof(*ht->buckets) * ht->num_buckets);
ht->buckets[0] = &ht->null;
ht->num_elements = 0;
}
/* ------------------------------------------------------------------------- */
/*
* If the current bucket is uninitialized, initialize it
* by recursively copying information from the parent.
*
* We may have a situation where entry E is a parent to 2 other
* entries E' and E". If we split E into E and E', then the
* nodes meant for E" end up in E or E', either of which is
* wrong. To solve that problem, we walk down the whole chain,
* inserting the elements into the correct place.
*/
static void fixup (qse_htl_t *ht, qse_uint32_t entry)
{
qse_uint32_t parent_entry;
qse_htl_node_t** last, * cur;
qse_uint32_t thiss;
parent_entry = parent_of(entry);
/* parent_entry == entry if and only if entry == 0 */
if (!ht->buckets[parent_entry])
{
fixup(ht, parent_entry);
}
/*
* Keep walking down cur, trying to find entries that
* don't belong here any more. There may be multiple
* ones, so we can't have a naive algorithm...
*/
last = &ht->buckets[parent_entry];
thiss = parent_entry;
for (cur = *last; cur != &ht->null; cur = cur->next)
{
qse_uint32_t real_entry;
real_entry = cur->key & ht->mask;
if (real_entry != thiss)
{ /* ht->buckets[real_entry] == QSE_NULL */
*last = &ht->null;
ht->buckets[real_entry] = cur;
thiss = real_entry;
}
last = &(cur->next);
}
/*
* We may NOT have initialized this bucket, so do it now.
*/
if (!ht->buckets[entry]) ht->buckets[entry] = &ht->null;
}
/*
* This should be a power of two. Changing it to 4 doesn't seem
* to make any difference.
*/
#define GROW_FACTOR (2)
/*
* Grow the hash table.
*/
static void grow (qse_htl_t*ht)
{
qse_htl_node_t **buckets;
buckets = QSE_MMGR_ALLOC (ht->mmgr, QSE_SIZEOF(*buckets) * GROW_FACTOR * ht->num_buckets);
if (!buckets) return;
QSE_MEMCPY (buckets, ht->buckets, QSE_SIZEOF(*buckets) * ht->num_buckets);
QSE_MEMSET (&buckets[ht->num_buckets], 0, QSE_SIZEOF(*buckets) * ht->num_buckets);
QSE_MMGR_FREE (ht->mmgr, ht->buckets);
ht->buckets = buckets;
ht->num_buckets *= GROW_FACTOR;
ht->next_grow *= GROW_FACTOR;
ht->mask = ht->num_buckets - 1;
}
qse_htl_node_t *qse_htl_search (qse_htl_t* ht, const void* data)
{
qse_uint32_t key;
qse_uint32_t entry;
qse_uint32_t reversed;
key = ht->hasher(ht, data);
entry = key & ht->mask;
reversed = reverse(key);
if (!ht->buckets[entry]) fixup(ht, entry);
return list_find(ht, ht->buckets[entry], reversed, data);
}
qse_htl_node_t *qse_htl_heterosearch (qse_htl_t* ht, const void* data, qse_htl_hasher_t hasher, qse_htl_comper_t comper)
{
qse_uint32_t key;
qse_uint32_t entry;
qse_uint32_t reversed;
key = /*ht->*/hasher(ht, data);
entry = key & ht->mask;
reversed = reverse(key);
if (!ht->buckets[entry]) fixup(ht, entry);
return __list_find(ht, ht->buckets[entry], reversed, data, comper);
}
/*
* Insert data.
*/
qse_htl_node_t* qse_htl_insert (qse_htl_t* ht, void* data)
{
qse_uint32_t key;
qse_uint32_t entry;
qse_uint32_t reversed;
qse_htl_node_t *node;
key = ht->hasher(ht, data);
entry = key & ht->mask;
reversed = reverse(key);
if (!ht->buckets[entry]) fixup(ht, entry);
/*
* If we try to do our own memory allocation here, the
* speedup is only ~15% or so, which isn't worth it.
*/
node = QSE_MMGR_ALLOC(ht->mmgr, QSE_SIZEOF(*node));
if (!node) return QSE_NULL;
QSE_MEMSET (node, 0, QSE_SIZEOF(*node));
node->next = &ht->null;
node->reversed = reversed;
node->key = key;
if (ht->copier)
{
node->data = ht->copier (ht, data);
if (!node->data)
{
QSE_MMGR_FREE (ht->mmgr, node);
return QSE_NULL;
}
}
else node->data = data;
/* already in the table, can't insert it */
if (!list_insert(ht, &ht->buckets[entry], node))
{
if (ht->freeer) ht->freeer (ht, node->data);
QSE_MMGR_FREE (ht->mmgr, node);
return QSE_NULL;
}
/*
* Check the load factor, and grow the table if
* necessary.
*/
ht->num_elements++;
if (ht->num_elements >= ht->next_grow) grow(ht);
return node;
}
/*
* Replace old data with new data, OR insert if there is no old.
*/
qse_htl_node_t* qse_htl_upsert (qse_htl_t* ht, void* data)
{
qse_htl_node_t *node;
void* datap;
node = qse_htl_search(ht, data);
if (!node) return qse_htl_insert(ht, data);
if (ht->copier)
{
datap = ht->copier (ht, data);
if (!datap) return QSE_NULL;
}
else datap = data;
if (ht->freeer) ht->freeer(ht, node->data);
node->data = datap;
return node;
}
qse_htl_node_t* qse_htl_update (qse_htl_t* ht, void* data)
{
qse_htl_node_t *node;
void* datap;
node = qse_htl_search(ht, data);
if (!node) return QSE_NULL;
if (ht->copier)
{
datap = ht->copier (ht, data);
if (!datap) return QSE_NULL;
}
else datap = data;
if (ht->freeer) ht->freeer(ht, node->data);
node->data = datap;
return node;
}
qse_htl_node_t* qse_htl_upyank (qse_htl_t* ht, void* data, void** olddata)
{
qse_htl_node_t* node;
2018-01-28 04:58:56 +00:00
void* datap;
node = qse_htl_search(ht, data);
if (!node) return QSE_NULL;
if (ht->copier)
{
datap = ht->copier (ht, data);
if (!datap) return QSE_NULL;
}
else datap = data;
*olddata = node->data;
node->data = datap;
return node;
}
qse_htl_node_t* qse_htl_ensert (qse_htl_t* ht, void* data)
{
qse_htl_node_t* node;
node = qse_htl_search(ht, data);
if (!node) node = qse_htl_insert(ht, data);
return node;
}
qse_htl_node_t* qse_htl_yanknode (qse_htl_t* ht, void* data)
{
qse_uint32_t key;
qse_uint32_t entry;
qse_uint32_t reversed;
qse_htl_node_t* node;
key = ht->hasher(ht, data);
entry = key & ht->mask;
reversed = reverse(key);
if (!ht->buckets[entry]) fixup(ht, entry);
node = list_find(ht, ht->buckets[entry], reversed, data);
if (!node) return QSE_NULL;
list_delete(ht, &ht->buckets[entry], node);
ht->num_elements--;
return node;
}
/*
* Yank an entry from the hash table, without freeing the data.
*/
void* qse_htl_yank (qse_htl_t* ht, void* data)
{
qse_htl_node_t* node;
void* old;
node = qse_htl_yanknode (ht, data);
if (!node) return QSE_NULL;
old = node->data;
QSE_MMGR_FREE (ht->mmgr, node);
return old;
}
/*
* Delete a piece of data from the hash table.
*/
int qse_htl_delete(qse_htl_t *ht, void* data)
{
void* old;
old = qse_htl_yank(ht, data);
if (!old) return -1;
if (ht->freeer) ht->freeer(ht, old);
return 0;
}
/*
* Walk over the nodes, allowing deletes & inserts to happen.
*/
void qse_htl_walk (qse_htl_t *ht, qse_htl_walker_t walker, void *ctx)
{
int i;
for (i = ht->num_buckets - 1; i >= 0; i--)
{
qse_htl_node_t* node, * next;
/*
* Ensure that the current bucket is filled.
*/
if (!ht->buckets[i]) fixup(ht, i);
for (node = ht->buckets[i]; node != &ht->null; node = next)
{
next = node->next;
if (walker(ht, node->data, ctx) == QSE_HTL_WALK_STOP) return;
}
}
}
/* ------------------------------------------------------------------------- */
#if 0
/*
* Find data from a template
*/
void *qse_htl_finddata(qse_htl_t *ht, const void *data)
{
qse_htl_node_t *node;
node = qse_htl_find(ht, data);
if (!node) return NULL;
return node->data;
}
#ifdef TESTING
/*
* Show what the hash table is doing.
*/
int qse_htl_info(qse_htl_t *ht)
{
int i, a, collisions, uninitialized;
int array[256];
if (!ht) return 0;
uninitialized = collisions = 0;
memset(array, 0, sizeof(array));
for (i = 0; i < ht->num_buckets; i++) {
qse_uint32_t key;
int load;
qse_htl_node_t *node, *next;
/*
* If we haven't inserted or looked up an entry
* in a bucket, it's uninitialized.
*/
if (!ht->buckets[i]) {
uninitialized++;
continue;
}
load = 0;
key = ~0;
for (node = ht->buckets[i]; node != &ht->null; node = next) {
if (node->reversed == key) {
collisions++;
} else {
key = node->reversed;
}
next = node->next;
load++;
}
if (load > 255) load = 255;
array[load]++;
}
printf("HASH TABLE %p\tbuckets: %d\t(%d uninitialized)\n", ht,
ht->num_buckets, uninitialized);
printf("\tnum entries %d\thash collisions %d\n",
ht->num_elements, collisions);
a = 0;
for (i = 1; i < 256; i++) {
if (!array[i]) continue;
printf("%d\t%d\n", i, array[i]);
/*
* Since the entries are ordered, the lookup cost
* for any one element in a chain is (on average)
* the cost of walking half of the chain.
*/
if (i > 1) {
a += array[i] * i;
}
}
a /= 2;
a += array[1];
printf("\texpected lookup cost = %d/%d or %f\n\n",
ht->num_elements, a,
(float) ht->num_elements / (float) a);
return 0;
}
#endif
#endif
/* ------------------------------------------------------------------------- */
#define FNV_MAGIC_INIT (0x811c9dc5)
#define FNV_MAGIC_PRIME (0x01000193)
/*
* A fast hash function. For details, see:
*
* http://www.isthe.com/chongo/tech/comp/fnv/
*
* Which also includes public domain source. We've re-written
* it here for our purposes.
*/
/*
* Continue hashing data.
*/
QSE_INLINE qse_uint32_t qse_genhash32_update (const void* data, qse_size_t size, qse_uint32_t hash)
{
const qse_uint8_t *p = data;
const qse_uint8_t *q = p + size;
/*
* FNV-1 hash each octet in the buffer
*/
while (p != q)
{
/*
* XOR the 8-bit quantity into the bottom of
* the hash.
*/
hash ^= (qse_uint32_t) (*p++);
/*
* Multiple by 32-bit magic FNV prime, mod 2^32
*/
hash *= FNV_MAGIC_PRIME;
#if 0
/*
* Potential optimization.
*/
hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
#endif
}
return hash;
}
qse_uint32_t qse_genhash32 (const void *data, qse_size_t size)
{
2019-03-01 09:27:03 +00:00
return qse_genhash32_update(data, size, FNV_MAGIC_INIT);
}
/*
* Hash a C string, so we loop over it once.
*/
qse_uint32_t qse_mbshash32 (const qse_mchar_t* p)
{
qse_uint32_t hash = FNV_MAGIC_INIT;
while (*p)
{
2019-03-01 09:27:03 +00:00
hash ^= (qse_uint32_t)(*p++);
hash *= FNV_MAGIC_PRIME;
}
return hash;
}
qse_uint32_t qse_wcshash32 (const qse_wchar_t* p)
{
qse_uint32_t hash = FNV_MAGIC_INIT;
while (*p)
{
#if (QSE_SIZEOF_WCHAR_T <= QSE_SIZEOF_UINT32_T)
hash ^= (qse_uint32_t)(*p);
hash *= FNV_MAGIC_PRIME;
#else
2019-03-01 09:27:03 +00:00
hash = qse_genhash_update(*p, QSE_SIZEOF(*p), hash);
#endif
p++;
}
return hash;
}
qse_uint32_t qse_mbscasehash32 (const qse_mchar_t* p)
{
qse_uint32_t hash = FNV_MAGIC_INIT;
while (*p)
{
qse_mchar_t mc = *p++;
mc = QSE_TOMLOWER(mc);
hash ^= (qse_uint32_t)mc;
hash *= FNV_MAGIC_PRIME;
}
return hash;
}
qse_uint32_t qse_wcscasehash32 (const qse_wchar_t* p)
{
qse_uint32_t hash = FNV_MAGIC_INIT;
while (*p)
{
qse_wchar_t wc = *p++;
wc = QSE_TOWLOWER(wc);
#if (QSE_SIZEOF_WCHAR_T <= QSE_SIZEOF_UINT32_T)
hash ^= (qse_uint32_t)(wc);
hash *= FNV_MAGIC_PRIME;
#else
2019-03-05 12:06:13 +00:00
hash = qse_genhash_update(&wc, QSE_SIZEOF(wc), hash);
#endif
}
return hash;
}
#if 0
/*
* Return a "folded" hash, where the lower "bits" are the
* hash, and the upper bits are zero.
*
* If you need a non-power-of-two hash, cope.
*/
qse_uint32_t qse_foldhash32 (qse_uint32_t hash, int bits)
{
int count;
qse_uint32_t result;
if ((bits <= 0) || (bits >= 32)) return hash;
result = hash;
/*
* Never use the same bits twice in an xor.
*/
for (count = 0; count < 32; count += bits)
{
hash >>= bits;
result ^= hash;
}
return result & (((qse_uint32_t) (1 << bits)) - 1);
}
#endif