calcurse-edge/src/htable.h
Lukas Fleischer 694d28eb78 Use tabs instead of spaces for indentation
This completes our switch to the Linux kernel coding style. Note that we
still use deeply nested constructs at some places which need to be fixed
up later.

Converted using the `Lindent` script from the Linux kernel code base,
along with some manual fixes.

Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
2013-04-14 00:19:01 +02:00

393 lines
25 KiB
C

/*
* Copyright (c) 2004-2013 calcurse Development Team <misc@calcurse.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef HTABLE_H
#define HTABLE_H
#include <stdint.h>
#include <string.h>
/*
* This file defines data structures for hash tables.
*
* Hash tables are ideal for applications with datasets needing lots of adding,
* searching or removal, as those are normally constant-time operations.
* The primary operation it supports efficiently is a lookup: given a key (e.g.
* a person's name), find the corresponding value (e.g. that person's telephone
* number). It works by transforming the key using a hash function into a hash,
* a number that is used as an index in an array to locate the desired location
* ("bucket") where the values should be.
*
* Hash tables support the efficient insertion of new entries, in expected O(1)
* time. The time spent in searching depends on the hash function and the load
* of the hash table; both insertion and search approach O(1) time with well
* chosen values and hashes.
*
* The collision resolution technique used here is direct chaining, implemented
* using singly linked lists (the worst-case time is O(n)).
*
* This was chosen because performance degradation is linear as the table
* fills, so there is almost no need to resize table
* (for example, a chaining hash table containing twice its recommended
* capacity of data would only be about twice as slow on average as the same
* table at its recommended capacity).
*/
#define HTABLE_HEAD(name, size, type) \
struct name { \
uint32_t noitems; /* Number of items stored in hash table. */ \
uint32_t nosingle; /* Number of items alone in their bucket. */ \
uint32_t nofreebkts; /* Number of free buckets. */ \
struct type *bkts[size]; /* Pointers to user-defined data structures. */ \
}
#define HTABLE_ENTRY(type) \
struct type *next /* To build the bucket chain list. */
#define HTABLE_SIZE(head) \
(sizeof (*(head)->bkts) ? sizeof ((head)->bkts) / sizeof (*(head)->bkts) : 0)
#define HTABLE_COUNT(head) \
((head)->noitems ? (head)->noitems : 0)
#define HTABLE_EMPTY(head) \
(HTABLE_COUNT((head)) == 0 ? 1 : 0)
#define HTABLE_COLLS(head) \
((head)->noitems ? 100.0 - 100 * (head)->nosingle / (head)->noitems : 0)
#define HTABLE_LOAD(head) \
(HTABLE_SIZE((head)) ? \
100.0 - 100.0 * (head)->nofreebkts / HTABLE_SIZE((head)) : 0)
#define HTABLE_INITIALIZER(head) \
{ 0, /* noitems */ \
0, /* nosingle */ \
HTABLE_SIZE((head)) /* nofreebkts */ \
}
#define HTABLE_INIT(head) do { \
bzero ((head), sizeof (*(head))); \
(head)->nofreebkts = HTABLE_SIZE((head)); \
} while (0)
/*
* Generate prototypes.
*/
#define HTABLE_PROTOTYPE(name, type) \
struct type *name##_HTABLE_INSERT(struct name *, struct type *); \
struct type *name##_HTABLE_REMOVE(struct name *, struct type *); \
struct type *name##_HTABLE_LOOKUP(struct name *, struct type *); \
uint32_t name##_HTABLE_FIND_BKT(struct name *, struct type *); \
int name##_HTABLE_CHAIN_LEN(struct name *, uint32_t); \
struct type *name##_HTABLE_FIRST_FROM(struct name *, int); \
struct type *name##_HTABLE_NEXT(struct name *, struct type *);
/*
* Generate function bodies.
*/
#define HTABLE_GENERATE(name, type, key, cmp) \
uint32_t \
name##_HTABLE_FIND_BKT(struct name *head, struct type *elm) \
{ \
uint32_t __bkt; \
const char *__key; \
int __len; \
\
(key) (elm, &__key, &__len); \
HTABLE_HASH(__key, __len, HTABLE_SIZE(head), __bkt); \
\
return __bkt; \
} \
\
int \
name##_HTABLE_CHAIN_LEN(struct name *head, uint32_t bkt) \
{ \
struct type *__bktp; \
int __len; \
\
__len = 0; \
for (__bktp = (head)->bkts[(bkt)]; __bktp != NULL; __bktp = __bktp->next) \
__len++; \
\
return __len; \
} \
\
struct type * \
name##_HTABLE_INSERT(struct name *head, struct type *elm) \
{ \
struct type *__bktp, **__bktpp; \
uint32_t __bkt, __pos; \
\
__pos = 0; \
__bkt = name##_HTABLE_FIND_BKT(head, elm); \
__bktpp = &head->bkts[__bkt]; \
while ((__bktp = *__bktpp)) \
{ \
if (!(cmp)(elm, __bktp)) \
return NULL; \
else \
{ \
__pos++; \
__bktpp = &__bktp->next; \
} \
} \
__bktp = elm; \
__bktp->next = NULL; \
*__bktpp = __bktp; \
head->noitems++; \
switch (__pos) \
{ \
case 0: \
head->nosingle++; \
head->nofreebkts--; \
break; \
case 1: \
head->nosingle--; \
break; \
default: \
break; \
} \
\
return __bktp; \
} \
\
struct type * \
name##_HTABLE_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__bktp, **__bktpp; \
uint32_t __bkt, __pos; \
\
__pos = 0; \
__bkt = name##_HTABLE_FIND_BKT(head, elm); \
__bktpp = &head->bkts[__bkt]; \
while ((__bktp = *__bktpp)) \
{ \
if (!(cmp)(elm, __bktp)) \
{ \
*__bktpp = __bktp->next; \
elm = __bktp; \
head->noitems--; \
if (__pos <= 1) /* Need to scan list to know if we have */ \
{ /* a free bucket or a single item. */ \
int __len; \
\
__len = name##_HTABLE_CHAIN_LEN(head, __bkt); \
switch (__len) \
{ \
case 0: \
head->nofreebkts++; \
head->nosingle--; \
break; \
case 1: \
head->nosingle++; \
break; \
} \
} \
return elm; \
} \
__pos++; \
__bktpp = &__bktp->next; \
} \
return NULL; \
} \
\
struct type * \
name##_HTABLE_LOOKUP(struct name *head, struct type *elm) \
{ \
struct type *__bktp, **__bktpp; \
uint32_t __bkt; \
\
__bkt = name##_HTABLE_FIND_BKT(head, elm); \
__bktpp = &head->bkts[__bkt]; \
while ((__bktp = *__bktpp)) \
{ \
if (!(cmp)(elm, __bktp)) \
return __bktp; \
else \
__bktpp = &__bktp->next; \
} \
\
return NULL; \
} \
\
struct type * \
name##_HTABLE_FIRST_FROM(struct name *head, int bkt) \
{ \
struct type *__bktp; \
\
while (bkt < HTABLE_SIZE(head)) \
{ \
if ((__bktp = head->bkts[bkt])) \
return __bktp; \
else \
bkt++; \
} \
\
return NULL; \
} \
\
struct type * \
name##_HTABLE_NEXT(struct name *head, struct type *elm) \
{ \
struct type *__elmp, *__bktp, **__bktpp; \
uint32_t __bkt; \
\
__elmp = NULL; \
__bkt = name##_HTABLE_FIND_BKT(head, elm); \
__bktpp = &head->bkts[__bkt]; \
while ((__bktp = *__bktpp)) \
{ \
if (!(cmp)(elm, __bktp)) \
{ \
__elmp = __bktp; \
break; \
} \
else \
__bktpp = &__bktp->next; \
} \
\
if (!__elmp) \
return NULL; \
else if (__elmp->next) \
return __elmp->next; \
else \
return name##_HTABLE_FIRST_FROM(head, ++__bkt); \
}
#define FIRST_BKT 0
#define HTABLE_INSERT(name, x, y) name##_HTABLE_INSERT(x, y)
#define HTABLE_REMOVE(name, x, y) name##_HTABLE_REMOVE(x, y)
#define HTABLE_LOOKUP(name, x, y) name##_HTABLE_LOOKUP(x, y)
#define HTABLE_FIRST_FROM(name, x, y) (HTABLE_EMPTY(x) ? NULL \
: name##_HTABLE_FIRST_FROM(x, y))
#define HTABLE_FIRST(name, x) HTABLE_FIRST_FROM(name, x, FIRST_BKT)
#define HTABLE_NEXT(name, x, y) (HTABLE_EMPTY(x) ? NULL \
: name##_HTABLE_NEXT(x, y))
#define HTABLE_FOREACH(x, name, head) \
for ((x) = HTABLE_FIRST(name, head); \
(x) != NULL; \
(x) = HTABLE_NEXT(name, head, x))
/*
* Hash functions.
*/
#ifdef HASH_FUNCTION
#define HTABLE_HASH HASH_FUNCTION
#else
#define HTABLE_HASH HASH_JEN
#endif
#define HASH_JEN_MIX(a, b, c) do { \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
} while (0)
#define HASH_JEN(key, keylen, num_bkts, bkt) do { \
register uint32_t i, j, k, hash; \
\
hash = 0xfeedbeef; \
i = j = 0x9e3779b9; \
k = keylen; \
while (k >= 12) \
{ \
i += (key[0] + ((unsigned)key[1] << 8) \
+ ((unsigned)key[2] << 16) \
+ ((unsigned)key[3] << 24)); \
j += (key[4] + ((unsigned)key[5] << 8) \
+ ((unsigned)key[6] << 16) \
+ ((unsigned)key[7] << 24 )); \
hash += (key[8] + ((unsigned)key[9] << 8) \
+ ((unsigned)key[10] << 16) \
+ ((unsigned)key[11] << 24)); \
\
HASH_JEN_MIX (i, j, hash); \
\
key += 12; \
k -= 12; \
} \
hash += keylen; \
switch (k) \
{ \
case 11: \
hash += ((unsigned)key[10] << 24); \
case 10: \
hash += ((unsigned)key[9] << 16); \
case 9: \
hash += ((unsigned)key[8] << 8); \
case 8: \
j += ((unsigned)key[7] << 24); \
case 7: \
j += ((unsigned)key[6] << 16); \
case 6: \
j += ((unsigned)key[5] << 8); \
case 5: \
j += key[4]; \
case 4: \
i += ((unsigned)key[3] << 24); \
case 3: \
i += ((unsigned)key[2] << 16); \
case 2: \
i += ((unsigned)key[1] << 8); \
case 1: \
i += key[0]; \
} \
HASH_JEN_MIX (i, j, hash); \
bkt = hash % (num_bkts); \
} while (0)
#define HASH_OAT(key, keylen, num_bkts, bkt) do { \
register uint32_t hash; \
int i; \
\
hash = 0; \
for (i = 0; i < keylen; i++) \
{ \
hash += key[i]; \
hash += (hash << 10); \
hash ^= (hash >> 6); \
} \
hash += (hash << 3); \
hash ^= (hash >> 11); \
hash += (hash << 15); \
bkt = hash % (num_bkts); \
} while (0)
#endif /* !HTABLE_H */