You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
15 KiB
278 lines
15 KiB
#ifndef TEMPLATE_H
|
|
#define TEMPLATE_H
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "memory.h"
|
|
|
|
#undef assert
|
|
/* gcc-3.0 sucks */
|
|
|
|
#if defined(DEBUG) && defined(NDEBUG)
|
|
#error "Can't debug and notdebug both"
|
|
#endif
|
|
|
|
#if !defined(DEBUG) && !defined(NDEBUG)
|
|
#define NDEBUG
|
|
#endif
|
|
|
|
#ifdef NDEBUG
|
|
# define DEBUG_ONLY( stmt )
|
|
# define assert(x) (void)0
|
|
#else
|
|
# define DEBUG_ONLY( stmt ) stmt
|
|
# define assert(x) ((x) ? 1 : _myassertbug(__LINE__, __FILE__, #x))
|
|
|
|
extern int _myassertbug(int line, char *file, char *err);
|
|
|
|
#endif
|
|
|
|
#ifdef __STRICT_ANSI__
|
|
#define inline __inline__
|
|
#endif
|
|
|
|
static inline unsigned long strhash(const char *x, unsigned char pow) {
|
|
unsigned long i = 0;
|
|
while (*x) {
|
|
i = (i * 39 + *x) % (1UL << pow);
|
|
x++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
#define KEEP(TYPE) ((void(*)(TYPE))NULL)
|
|
|
|
#define LIST(NAME,TYPE) \
|
|
typedef struct NAME NAME; \
|
|
struct NAME { TYPE value; struct NAME *next; }; \
|
|
void insert_##NAME(NAME **where, TYPE v); \
|
|
void insert_l_##NAME(NAME **where, TYPE v, int line); \
|
|
TYPE remove_##NAME(NAME **where); \
|
|
void delete_##NAME(NAME **where); \
|
|
void free_##NAME(NAME *l)
|
|
|
|
|
|
#define LIST_IMPL_2(NAME,TYPE,FREE,LFREE) \
|
|
TYPE remove_##NAME(NAME **where) { \
|
|
NAME *next; \
|
|
TYPE res; \
|
|
assert(*where != NULL); \
|
|
next = (*where)->next; \
|
|
res = (*where)->value; \
|
|
LFREE(*where, sizeof(NAME)); \
|
|
*where = next; \
|
|
return res; \
|
|
} \
|
|
void delete_##NAME(NAME **where) { \
|
|
NAME *next; \
|
|
assert(*where != NULL); \
|
|
next = (*where)->next; \
|
|
if (FREE != NULL) (FREE)((*where)->value); \
|
|
LFREE(*where, sizeof(NAME)); \
|
|
*where = next; \
|
|
} \
|
|
void free_##NAME(NAME *l) { \
|
|
NAME *n; \
|
|
while (l != NULL) { \
|
|
n = l->next; \
|
|
if (FREE != NULL) (FREE)(l->value); \
|
|
LFREE(l, sizeof(NAME)); \
|
|
l = n; \
|
|
} \
|
|
}
|
|
|
|
#define LIST_IMPL(NAME,TYPE,FREE,LMALLOC,LFREE) \
|
|
void insert_##NAME(NAME **where, TYPE v) { \
|
|
NAME *n = *where; \
|
|
*where = LMALLOC(sizeof(NAME)); \
|
|
if (*where == NULL) \
|
|
die("insert_" #NAME " malloc:"); \
|
|
(*where)->value = v; \
|
|
(*where)->next = n; \
|
|
} \
|
|
LIST_IMPL_2(NAME,TYPE,FREE,LFREE)
|
|
|
|
#define LIST_IMPLX(NAME,TYPE,FREE) \
|
|
void insert_l_##NAME(NAME **where, TYPE v, int line) { \
|
|
NAME *n = *where; \
|
|
*where = block_malloc2(sizeof(NAME), line); \
|
|
if (*where == NULL) \
|
|
die("insert_" #NAME " malloc:"); \
|
|
(*where)->value = v; \
|
|
(*where)->next = n; \
|
|
} \
|
|
LIST_IMPL_2(NAME,TYPE,FREE,block_free)
|
|
|
|
#define HASH(TYPE, KEY, VALUE) \
|
|
typedef struct TYPE TYPE; \
|
|
typedef struct TYPE##_iter TYPE##_iter; \
|
|
struct TYPE##_iter { \
|
|
unsigned long i; TYPE *h; KEY k; VALUE v; \
|
|
}; \
|
|
TYPE *new_##TYPE(void); \
|
|
void free_##TYPE(TYPE *h); \
|
|
TYPE##_iter first_##TYPE(TYPE *h); \
|
|
TYPE##_iter next_##TYPE(TYPE##_iter i); \
|
|
int done_##TYPE(TYPE##_iter i); \
|
|
\
|
|
void iterate_##TYPE(TYPE *h, void (*itf)(TYPE*,VALUE,void*), \
|
|
void *data); \
|
|
VALUE lookup_##TYPE(TYPE *h, KEY k); \
|
|
void add_##TYPE(TYPE *h, KEY k, VALUE v); \
|
|
VALUE replace_##TYPE(TYPE *h, KEY k, VALUE v); \
|
|
VALUE remove_##TYPE(TYPE *h, KEY k)
|
|
|
|
#define HASH_MAGIC (0x22DEAD22)
|
|
|
|
#define HASH_IMPL(TYPE, KEY, VALUE, POW2, HASH, CMP, FREEK, FREEV) \
|
|
struct TYPE { \
|
|
unsigned long magic; \
|
|
unsigned long size; \
|
|
unsigned long n_used; \
|
|
unsigned long n_collisions; \
|
|
struct { KEY key; VALUE value; } *hash; \
|
|
}; \
|
|
TYPE *new_##TYPE(void) { \
|
|
size_t i; \
|
|
TYPE *h = malloc(sizeof(TYPE)); \
|
|
if (h == NULL) die("new_" #TYPE " malloc:"); \
|
|
\
|
|
h->magic = HASH_MAGIC; \
|
|
h->size = (1 << POW2); \
|
|
h->n_used = 0; \
|
|
h->n_collisions = 0; \
|
|
h->hash = malloc(sizeof(*h->hash) * h->size ); \
|
|
if (h == NULL) die("new_" #TYPE " hash malloc:"); \
|
|
\
|
|
for (i = 0; i < h->size; i++) { \
|
|
h->hash[i].key = NULL; \
|
|
h->hash[i].value = NULL; \
|
|
} \
|
|
\
|
|
return h; \
|
|
} \
|
|
\
|
|
void free_##TYPE(TYPE *h) { \
|
|
size_t i; \
|
|
if (h == NULL) return; \
|
|
assert(h->magic == HASH_MAGIC); \
|
|
/* printf("Freeing: size: %lu used: %lu coll: %lu\n", */ \
|
|
/* h->size, h->n_used, h->n_collisions); */ \
|
|
h->magic = ~HASH_MAGIC; \
|
|
for (i = 0; i < h->size; i++) { \
|
|
if (FREEK && h->hash[i].key) \
|
|
(FREEK)(h->hash[i].key); \
|
|
if (FREEV && h->hash[i].value) \
|
|
(FREEV)(h->hash[i].value); \
|
|
} \
|
|
free(h->hash); \
|
|
free(h); \
|
|
} \
|
|
\
|
|
void iterate_##TYPE(TYPE *h, void (*itf)(TYPE*,VALUE,void*), \
|
|
void *data) \
|
|
{ \
|
|
TYPE##_iter x; \
|
|
for (x = first_##TYPE(h); \
|
|
!done_##TYPE(x); \
|
|
x = next_##TYPE(x)) \
|
|
{ \
|
|
itf(h, x.v, data); \
|
|
} \
|
|
} \
|
|
\
|
|
TYPE##_iter first_##TYPE(TYPE *h) { \
|
|
TYPE##_iter i; \
|
|
i.i = 0; \
|
|
i.h = h; \
|
|
return next_##TYPE(i); \
|
|
} \
|
|
\
|
|
TYPE##_iter next_##TYPE(TYPE##_iter i) { \
|
|
assert(i.h->magic == HASH_MAGIC); \
|
|
while(i.i < i.h->size) { \
|
|
if (i.h->hash[i.i].value != NULL) { \
|
|
i.k = i.h->hash[i.i].key; \
|
|
i.v = i.h->hash[i.i].value; \
|
|
i.i++; \
|
|
return i; \
|
|
} \
|
|
i.i++; \
|
|
} \
|
|
i.h = NULL; \
|
|
return i; \
|
|
} \
|
|
\
|
|
int done_##TYPE(TYPE##_iter i) { \
|
|
assert(i.h == NULL || i.h->magic == HASH_MAGIC); \
|
|
assert(i.h == NULL || (i.k != NULL && i.v != NULL)); \
|
|
assert(i.h == NULL || (0 < i.i && i.i <= i.h->size)); \
|
|
return i.h == NULL; \
|
|
} \
|
|
\
|
|
VALUE lookup_##TYPE(TYPE *h, KEY k) { \
|
|
int i = HASH(k, POW2); \
|
|
assert(h->magic == HASH_MAGIC); \
|
|
assert(h->n_used < h->size); /* ensure termination */ \
|
|
while(h->hash[i].key) { \
|
|
if ((CMP)(h->hash[i].key, k) == 0) { \
|
|
if (h->hash[i].value != NULL) { \
|
|
return h->hash[i].value; \
|
|
} \
|
|
} \
|
|
i = (i + 1) % (1 << POW2); \
|
|
} \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
void add_##TYPE(TYPE *h, KEY k, VALUE v) { \
|
|
int i = HASH(k, POW2); \
|
|
assert(h->magic == HASH_MAGIC); \
|
|
assert(h->n_used < h->size); /* ensure termination */ \
|
|
assert(v != NULL); \
|
|
while(h->hash[i].value) { \
|
|
assert((CMP)(h->hash[i].key, k) != 0); \
|
|
i = (i + 1) % (1 << POW2); \
|
|
h->n_collisions++; \
|
|
} \
|
|
if (FREEK != NULL && h->hash[i].key) \
|
|
FREEK(h->hash[i].key); \
|
|
h->n_used++; \
|
|
h->hash[i].key = k; \
|
|
h->hash[i].value = v; \
|
|
} \
|
|
\
|
|
VALUE replace_##TYPE(TYPE *h, KEY k, VALUE v) { \
|
|
VALUE tmp; \
|
|
int i = HASH(k,POW2); \
|
|
assert(h->magic == HASH_MAGIC); \
|
|
assert(v != NULL); \
|
|
while(h->hash[i].key) { \
|
|
if ((CMP)(h->hash[i].key, k) == 0) break; \
|
|
i = (i + 1) % (1 << POW2); \
|
|
} \
|
|
assert(h->hash[i].value != NULL); \
|
|
tmp = h->hash[i].value; \
|
|
h->hash[i].key = k; \
|
|
h->hash[i].value = v; \
|
|
return tmp; \
|
|
} \
|
|
\
|
|
VALUE remove_##TYPE(TYPE *h, KEY k) { \
|
|
VALUE tmp; \
|
|
int i = HASH(k, POW2); \
|
|
assert(h->magic == HASH_MAGIC); \
|
|
while(h->hash[i].key) { \
|
|
if ((CMP)(h->hash[i].key, k) == 0) break; \
|
|
i = (i + 1) % (1 << POW2); \
|
|
} \
|
|
tmp = h->hash[i].value; \
|
|
h->hash[i].value = NULL; \
|
|
if (tmp != NULL) h->n_used--; \
|
|
return tmp; \
|
|
}
|
|
|
|
#endif
|
|
|
|
|