mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-01-11 23:53:19 +00:00
168 lines
4.1 KiB
C
168 lines
4.1 KiB
C
/*
|
|
* hashtab.c
|
|
* Copyright (C) 2013-2015 Mikael Pettersson
|
|
*
|
|
* This file is part of pdp10-tools.
|
|
*
|
|
* pdp10-tools 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* pdp10-tools 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 pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "hashtab.h"
|
|
|
|
static struct hashnode **hashtab_alloc_bucket(unsigned int nrelem)
|
|
{
|
|
size_t nrbytes;
|
|
struct hashnode **bucket;
|
|
unsigned int i;
|
|
|
|
nrbytes = nrelem * sizeof(struct hashnode*);
|
|
bucket = malloc(nrbytes);
|
|
if (!bucket) {
|
|
fprintf(stderr, "%s: malloc(%zu) failed: %s\n", __FUNCTION__, nrbytes, strerror(errno));
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < nrelem; ++i)
|
|
bucket[i] = NULL;
|
|
return bucket;
|
|
}
|
|
|
|
int hashtab_init(struct hashtab *hashtab, unsigned int log2size, hashtab_eq_func_t eq_func)
|
|
{
|
|
unsigned int size;
|
|
|
|
size = 1 << log2size;
|
|
hashtab->log2size = log2size;
|
|
hashtab->mask = size - 1;
|
|
hashtab->used = 0;
|
|
hashtab->eq_func = eq_func;
|
|
hashtab->bucket = hashtab_alloc_bucket(size);
|
|
return hashtab->bucket ? 0 : -1;
|
|
}
|
|
|
|
struct hashnode *hashtab_lookup(const struct hashtab *hashtab, uintptr_t hashval, const void *data)
|
|
{
|
|
unsigned int i;
|
|
struct hashnode *hashnode;
|
|
|
|
i = hashval & hashtab->mask;
|
|
|
|
hashnode = hashtab->bucket[i];
|
|
while (hashnode != NULL) {
|
|
if (hashnode->hashval == hashval
|
|
&& (hashtab->eq_func == NULL || (*hashtab->eq_func)(hashnode, data) != 0))
|
|
break;
|
|
hashnode = hashnode->next;
|
|
}
|
|
|
|
return hashnode;
|
|
}
|
|
|
|
static int hashtab_grow(struct hashtab *hashtab)
|
|
{
|
|
unsigned int old_size, new_size, new_mask, i;
|
|
struct hashnode **old_bucket, **new_bucket;
|
|
|
|
old_size = 1 << hashtab->log2size;
|
|
new_size = old_size << 1;
|
|
new_bucket = hashtab_alloc_bucket(new_size);
|
|
if (!new_bucket)
|
|
return -1;
|
|
|
|
old_bucket = hashtab->bucket;
|
|
hashtab->log2size += 1;
|
|
new_mask = new_size - 1;
|
|
hashtab->mask = new_mask;
|
|
hashtab->bucket = new_bucket;
|
|
|
|
for (i = 0; i < old_size; ++i) {
|
|
struct hashnode *hashnode = old_bucket[i];
|
|
while (hashnode != NULL) {
|
|
struct hashnode *next = hashnode->next;
|
|
unsigned int j = hashnode->hashval & new_mask;
|
|
hashnode->next = new_bucket[j];
|
|
new_bucket[j] = hashnode;
|
|
hashnode = next;
|
|
}
|
|
}
|
|
|
|
free(old_bucket);
|
|
return 0;
|
|
}
|
|
|
|
int hashtab_insert(struct hashtab *hashtab, struct hashnode *hashnode)
|
|
{
|
|
unsigned int i, size;
|
|
|
|
i = hashnode->hashval & hashtab->mask;
|
|
hashnode->next = hashtab->bucket[i];
|
|
hashtab->bucket[i] = hashnode;
|
|
hashtab->used += 1;
|
|
size = 1 << hashtab->log2size;
|
|
if (hashtab->used > (4 * size) / 5) /* rehash at 80% */
|
|
return hashtab_grow(hashtab);
|
|
return 0;
|
|
}
|
|
|
|
struct hashnode *hashtab_reset(struct hashtab *hashtab)
|
|
{
|
|
unsigned int i, size;
|
|
struct hashnode **bucket, *all_nodes, *head, *tail;
|
|
|
|
all_nodes = NULL;
|
|
bucket = hashtab->bucket;
|
|
size = 1 << hashtab->log2size;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
head = bucket[i];
|
|
if (head) {
|
|
bucket[i] = NULL;
|
|
tail = head;
|
|
while (tail->next)
|
|
tail = tail->next;
|
|
tail->next = all_nodes;
|
|
all_nodes = head;
|
|
}
|
|
}
|
|
|
|
hashtab->used = 0;
|
|
return all_nodes;
|
|
}
|
|
|
|
int hashtab_enumerate(const struct hashtab *hashtab,
|
|
int (*func)(struct hashnode *hashnode, void *data),
|
|
void *data)
|
|
{
|
|
unsigned int i, size;
|
|
struct hashnode **bucket, *head;
|
|
int status;
|
|
|
|
bucket = hashtab->bucket;
|
|
size = 1 << hashtab->log2size;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
head = bucket[i];
|
|
while (head != NULL) {
|
|
status = (*func)(head, data);
|
|
if (status != 0)
|
|
return status;
|
|
head = head->next;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|