ditch gpl hash table library for glib

This commit is contained in:
Sami Samhuri 2011-04-18 22:07:09 -07:00
parent 8439041407
commit 0bb7a2ea22
9 changed files with 35 additions and 462 deletions

View file

@ -1,8 +1,13 @@
CC=gcc -Wall -g
CC = gcc
CFLAGS := -Wall -g $(shell pkg-config --cflags glib-2.0)
LFLAGS := $(shell pkg-config --libs glib-2.0)
all: lake
lake: lake.o env.o hashtab.o int.o string.o sym.o parse.o bool.o list.o
lake: lake.o env.o int.o string.o sym.o parse.o bool.o list.o
$(CC) $(CFLAGS) $(LFLAGS) $? -o $@
# use touch to prevent errors in case files do not exist
clean:
rm *.o lake
@touch dummy.o lake
rm -f *.o lake

View file

@ -6,7 +6,9 @@ A quick and dirty scheme written in C, for fun and to use while reading The Litt
Compiling & Running
===================
Portable C, no deps, nothing to configure, no documentation!
Portable C, only dep is glib, nothing to configure, no documentation!
Install glib 2.x using your package manager, for me it's either `brew install glib` or `sudo aptitude install glib`.
$ make
$ ./lake

27
env.c
View file

@ -7,15 +7,12 @@
*
*/
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lake.h"
#include "env.h"
#include "hashtab.h"
#define ENV_TABLE_SIZE 64
#define SYM_TABLE_SIZE 1024
static Env *_shared = NULL;
@ -38,37 +35,39 @@ Env *env_make(Env *parent)
env = malloc(sizeof(Env));
if (!env) oom();
env->parent = parent;
env->bindings = ht_init(ENV_TABLE_SIZE, NULL);
env->symbols = ht_init(SYM_TABLE_SIZE, NULL);
env->bindings = g_hash_table_new(g_str_hash, g_str_equal);
env->symbols = g_hash_table_new(g_str_hash, g_str_equal);
return env;
}
LakeVal *env_define(Env *env, char *key, LakeVal *val)
{
size_t keylen = strlen(key);
if (ht_get(env->bindings, key, keylen) != NULL)
if (g_hash_table_lookup(env->bindings, key) != NULL)
return NULL;
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
val = VAL_OR_NIL(val);
g_hash_table_insert(env->bindings, key, val);
return val;
}
LakeVal *env_set(Env *env, char *key, LakeVal *val)
{
size_t keylen = strlen(key);
if (ht_get(env->bindings, key, keylen) == NULL)
if (g_hash_table_lookup(env->bindings, key) == NULL)
return NULL;
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
val = VAL_OR_NIL(val);
g_hash_table_insert(env->bindings, key, val);
return val;
}
LakeVal *env_get(Env *env, char *key)
{
return VAL_OR_NIL(ht_get(env->bindings, key, strlen(key)));
return VAL_OR_NIL(g_hash_table_lookup(env->bindings, key));
}
int env_is_bound(Env *env, char *key)
{
return (ht_get(env->bindings, key, strlen(key)) != NULL);
return g_hash_table_lookup(env->bindings, key) != NULL;
}
/*

6
env.h
View file

@ -10,12 +10,12 @@
#ifndef _LAKE_ENV_H
#define _LAKE_ENV_H 1
#include "hashtab.h"
#include <glib.h>
struct env {
struct env *parent;
hashtab_t *bindings;
hashtab_t *symbols;
GHashTable *bindings;
GHashTable *symbols;
};
typedef struct env Env;

310
hashtab.c
View file

@ -1,310 +0,0 @@
/* hashtab.c - Simple, Reliable C Hashtable
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashtab.h"
hashtab_t *ht_init (size_t size, int (*hash_func) (void *, size_t, size_t))
{
hashtab_t *new_ht = (hashtab_t *) malloc (sizeof (hashtab_t));
new_ht->arr = (hashtab_node_t **) malloc (sizeof (hashtab_node_t *) * size);
new_ht->size = size;
new_ht->count = 0;
/* all entries are empty */
int i = 0;
for (i = 0; i < (int) size; i++)
{
new_ht->arr[i] = NULL;
}
if (hash_func == NULL)
new_ht->hash_func = &ht_hash;
else
new_ht->hash_func = hash_func;
return new_ht;
}
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen)
{
int index = ht_hash (key, keylen, hashtable->size);
if (hashtable->arr[index] == NULL) {
return NULL;
}
hashtab_node_t *last_node = hashtable->arr[index];
while (last_node != NULL)
{
/* only compare matching keylens */
if (last_node->keylen == keylen)
{
/* compare keys */
if (memcmp (key, last_node->key, keylen) == 0)
{
return last_node->value;
}
}
last_node = last_node->next;
}
return NULL;
}
void *ht_put (hashtab_t * hashtable,
void *key, size_t keylen, void *value, size_t vallen)
{
int index = ht_hash (key, keylen, hashtable->size);
hashtab_node_t *next_node, *last_node;
next_node = hashtable->arr[index];
last_node = NULL;
/* Search for an existing key. */
while (next_node != NULL)
{
/* only compare matching keylens */
if (next_node->keylen == keylen)
{
/* compare keys */
if (memcmp (key, next_node->key, keylen) == 0)
{
/* this key already exists, replace it */
if (next_node->vallen != vallen)
{
/* new value is a different size */
free (next_node->value);
next_node->value = malloc (vallen);
if (next_node->value == NULL)
return NULL;
}
memcpy (next_node->value, value, vallen);
next_node->vallen = vallen;
return next_node->value;
}
}
last_node = next_node;
next_node = next_node->next;
}
/* create a new node */
hashtab_node_t *new_node;
new_node = (hashtab_node_t *) malloc (sizeof (hashtab_node_t));
if (new_node == NULL)
return NULL;
/* get some memory for the new node data */
new_node->key = malloc (keylen);
new_node->value = malloc (vallen);
if (new_node->key == NULL || new_node->key == NULL)
{
free (new_node->key);
free (new_node->value);
free (new_node);
return NULL;
}
/* copy over the value and key */
memcpy (new_node->key, key, keylen);
memcpy (new_node->value, value, vallen);
new_node->keylen = keylen;
new_node->vallen = vallen;
/* no next node */
new_node->next = NULL;
/* Tack the new node on the end or right on the table. */
if (last_node != NULL)
last_node->next = new_node;
else
hashtable->arr[index] = new_node;
hashtable->count++;
return new_node->value;
}
/* delete the given key from the hashtable */
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen)
{
hashtab_node_t *last_node, *next_node;
int index = ht_hash (key, keylen, hashtable->size);
next_node = hashtable->arr[index];
last_node = NULL;
while (next_node != NULL)
{
if (next_node->keylen == keylen)
{
/* compare keys */
if (memcmp (key, next_node->key, keylen) == 0)
{
/* free node memory */
free (next_node->value);
free (next_node->key);
/* adjust the list pointers */
if (last_node != NULL)
last_node->next = next_node->next;
else
hashtable->arr[index] = next_node->next;
/* free the node */
free (next_node);
break;
}
}
last_node = next_node;
next_node = next_node->next;
}
}
/* grow the hashtable */
void *ht_grow (hashtab_t * old_ht, size_t new_size)
{
/* create new hashtable */
hashtab_t *new_ht = ht_init (new_size, old_ht->hash_func);
if (new_ht == NULL)
return NULL;
void *ret; /* captures return values */
/* Iterate through the old hashtable. */
hashtab_iter_t ii;
ht_iter_init (old_ht, &ii);
for (; ii.key != NULL; ht_iter_inc (&ii))
{
ret = ht_put (new_ht, ii.key, ii.keylen, ii.value, ii.vallen);
if (ret == NULL)
{
/* Insert failed. Destroy new hashtable and return. */
ht_destroy (new_ht);
return NULL;
}
}
/* Destroy the old hashtable. */
ht_destroy (old_ht);
return new_ht;
}
/* free all resources used by the hashtable */
void ht_destroy (hashtab_t * hashtable)
{
hashtab_node_t *next_node, *last_node;
/* Free each linked list in hashtable. */
int i;
for (i = 0; i < (int) hashtable->size; i++)
{
next_node = hashtable->arr[i];
while (next_node != NULL)
{
/* destroy node */
free (next_node->key);
free (next_node->value);
last_node = next_node;
next_node = next_node->next;
free (last_node);
}
}
free (hashtable->arr);
free (hashtable);
}
/* iterator initilaize */
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii)
{
/* stick in initial bookeeping data */
ii->internal.hashtable = hashtable;
ii->internal.node = NULL;
ii->internal.index = -1;
/* have iterator point to first element */
ht_iter_inc (ii);
}
/* iterator increment */
void ht_iter_inc (hashtab_iter_t * ii)
{
hashtab_t *hashtable = ii->internal.hashtable;
int index = ii->internal.index;
/* attempt to grab the next node */
if (ii->internal.node == NULL || ii->internal.node->next == NULL)
index++;
else
{
/* next node in the list */
ii->internal.node = ii->internal.node->next;
ii->key = ii->internal.node->key;
ii->value = ii->internal.node->value;
ii->keylen = ii->internal.node->keylen;
ii->vallen = ii->internal.node->vallen;
return;
}
/* find next node */
while (hashtable->arr[index] == NULL && index < (int) hashtable->size)
index++;
if (index >= (int) hashtable->size)
{
/* end of hashtable */
ii->internal.node = NULL;
ii->internal.index = (int) hashtable->size;
ii->key = NULL;
ii->value = NULL;
ii->keylen = 0;
ii->vallen = 0;
return;
}
/* point to the next item in the hashtable */
ii->internal.node = hashtable->arr[index];
ii->internal.index = index;
ii->key = ii->internal.node->key;
ii->value = ii->internal.node->value;
ii->keylen = ii->internal.node->keylen;
ii->vallen = ii->internal.node->vallen;
}
int ht_hash (void *key, size_t keylen, size_t hashtab_size)
{
int sum = 0;
/* very simple hash function for now */
int i;
for (i = 0; i < (int) keylen; i++)
{
sum += ((unsigned char *) key)[i] * (i + 1);
}
return (sum % (int) hashtab_size);
}

120
hashtab.h
View file

@ -1,120 +0,0 @@
/* hashtab.h - Simple, Reliable C Hashtable
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/* I needed a hashtable for a project and wanted to code my own. This
* hashtable compromises speed for reliability, specifically with
* growing the hashtable.
*
* The hashtable does not grow automatically, but when the hashtable
* grow function is called. Growing the hashtable is a safe operation:
* if growing the hashtable fails, the existing hashtable is not
* destroyed or modified.
*
* This hashtable is not thread-safe.
*/
#ifndef HASHTAB_H
#define HASHTAB_H
#include <stdlib.h>
#include <string.h>
typedef struct hashtab_node_t
{
void *key; /* key for the node */
size_t keylen; /* length of the key */
void *value; /* value for this node */
size_t vallen; /* length of the value */
struct hashtab_node_t *next; /* next node (open hashtable) */
} hashtab_node_t;
typedef struct hashtab_t
{
hashtab_node_t **arr;
size_t size; /* size of the hash */
int count; /* number if items in this table */
int (*hash_func) (void *, size_t, size_t); /* hash function */
} hashtab_t;
/* Iterator type for iterating through the hashtable. */
typedef struct hashtab_iter_t
{
/* key and value of current item */
void *key;
void *value;
size_t keylen;
size_t vallen;
/* bookkeeping data */
struct hashtab_internal_t
{
hashtab_t *hashtable;
hashtab_node_t *node;
int index;
} internal;
} hashtab_iter_t;
/* Initialize a new hashtable (set bookingkeeping data) and return a
* pointer to the hashtable. A hash function may be provided. If no
* function pointer is given (a NULL pointer), then the built in hash
* function is used. A NULL pointer returned if the creation of the
* hashtable failed due to a failed malloc(). */
hashtab_t *ht_init (size_t size,
int (*hash_func)
(void *key, size_t keylen, size_t ht_size));
/* Fetch a value from table matching the key. Returns a pointer to
* the value matching the given key. */
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen);
/* Put a value into the table with the given key. Returns NULL if
* malloc() fails to allocate memory for the new node. */
void *ht_put (hashtab_t * hashtable,
void *key, size_t keylen, void *value, size_t vallen);
/* Delete the given key and value pair from the hashtable. If the key
* does not exist, no error is given. */
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen);
/* Change the size of the hashtable. It will allocate a new hashtable
* and move all keys and values over. The pointer to the new hashtable
* is returned. Will return NULL if the new hashtable fails to be
* allocated. If this happens, the old hashtable will not be altered
* in any way. The old hashtable is destroyed upon a successful
* grow. */
void *ht_grow (hashtab_t * hashtable, size_t new_size);
/* Free all resources used by the hashtable. */
void ht_destroy (hashtab_t * hashtable);
/* Initialize the given iterator. It will point to the first element
* in the hashtable. */
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii);
/* Increment the iterator to the next element. The iterator key and
* value will point to NULL values when the iterator has reached the
* end of the hashtable. */
void ht_iter_inc (hashtab_iter_t * ii);
/* Default hashtable hash function. */
int ht_hash (void *key, size_t key_size, size_t hashtab_size);
#endif

1
list.c
View file

@ -8,6 +8,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include "int.h"
#include "lake.h"
#include "list.h"

View file

@ -7,6 +7,7 @@
*
*/
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "int.h"
@ -61,8 +62,6 @@ char *str_val(LakeStr *str)
return strdup(str->s);
}
#define MIN(a, b) (a < b ? a : b)
LakeInt *str_cmp(LakeStr *a, LakeStr *b)
{
return int_from_c(strncmp(a->s, b->s, MIN(a->n, b->n)));

15
sym.c
View file

@ -7,12 +7,12 @@
*
*/
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "env.h"
#include "lake.h"
#include "hashtab.h"
#include "string.h"
#include "sym.h"
@ -28,15 +28,14 @@ static LakeSym *sym_alloc(void)
LakeSym *sym_intern(char *s)
{
size_t n = strlen(s);
hashtab_t *symbols = shared_env()->symbols;
LakeSym *sym = ht_get(symbols, s, n);
GHashTable *symbols = shared_env()->symbols;
LakeSym *sym = g_hash_table_lookup(symbols, s);
if (!sym) {
sym = sym_alloc();
sym->n = n;
sym->n = strlen(s);
sym->s = strdup(s);
sym->hash = ht_hash(s, n, symbols->size);
ht_put(symbols, sym->s, sym->n, sym, VAL_SIZE(sym));
sym->hash = g_str_hash(s);
g_hash_table_insert(symbols, sym->s, sym);
}
return sym;
}
@ -61,8 +60,6 @@ unsigned long sym_val(LakeSym *sym)
return sym->hash;
}
#define MIN(a, b) (a < b ? a : b)
LakeSym *sym_eq(LakeSym *a, LakeSym *b)
{
return bool_from_int(strncmp(a->s, b->s, MIN(a->n, b->n)) == 0);