Blame hash.c

Packit Service 80a84b
/*
Packit Service 80a84b
 * Borrowed from libkmod - interface to kernel module operations
Packit Service 80a84b
 *
Packit Service 80a84b
 * Copyright (C) 2011-2013  ProFUSION embedded systems
Packit Service 80a84b
 *
Packit Service 80a84b
 * This library is free software; you can redistribute it and/or
Packit Service 80a84b
 * modify it under the terms of the GNU Lesser General Public
Packit Service 80a84b
 * License as published by the Free Software Foundation; either
Packit Service 80a84b
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 80a84b
 *
Packit Service 80a84b
 * This library is distributed in the hope that it will be useful,
Packit Service 80a84b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 80a84b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 80a84b
 * Lesser General Public License for more details.
Packit Service 80a84b
 *
Packit Service 80a84b
 * You should have received a copy of the GNU Lesser General Public
Packit Service 80a84b
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service 80a84b
 */
Packit Service 80a84b
Packit Service 80a84b
#include <errno.h>
Packit Service 80a84b
#include <inttypes.h>
Packit Service 80a84b
#include <stdlib.h>
Packit Service 80a84b
#include <string.h>
Packit Service 80a84b
Packit Service 80a84b
#include "hash.h"
Packit Service 80a84b
Packit Service 80a84b
/* endianess and alignments                                                 */
Packit Service 80a84b
/* taken from kmod shared/util.h                                            */
Packit Service 80a84b
/* ************************************************************************ */
Packit Service 80a84b
#define get_unaligned(ptr)			\
Packit Service 80a84b
({						\
Packit Service 80a84b
	struct __attribute__((packed)) {	\
Packit Service 80a84b
		typeof(*(ptr)) __v;		\
Packit Service 80a84b
	} *__p = (typeof(__p)) (ptr);		\
Packit Service 80a84b
	__p->__v;				\
Packit Service 80a84b
})
Packit Service 80a84b
Packit Service 80a84b
#define put_unaligned(val, ptr)			\
Packit Service 80a84b
do {						\
Packit Service 80a84b
	struct __attribute__((packed)) {	\
Packit Service 80a84b
		typeof(*(ptr)) __v;		\
Packit Service 80a84b
	} *__p = (typeof(__p)) (ptr);		\
Packit Service 80a84b
	__p->__v = (val);			\
Packit Service 80a84b
} while (0)
Packit Service 80a84b
Packit Service 80a84b
static inline unsigned int ALIGN_POWER2(unsigned int u)
Packit Service 80a84b
{
Packit Service 80a84b
	return 1 << ((sizeof(u) * 8) - __builtin_clz(u - 1));
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
static inline size_t min(size_t a, size_t b)
Packit Service 80a84b
{
Packit Service 80a84b
	return a < b ? a : b;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
struct hash_entry {
Packit Service 80a84b
	const char *key;
Packit Service 80a84b
	size_t keylen;
Packit Service 80a84b
	const void *value;
Packit Service 80a84b
};
Packit Service 80a84b
Packit Service 80a84b
struct hash_bucket {
Packit Service 80a84b
	struct hash_entry *entries;
Packit Service 80a84b
	unsigned int used;
Packit Service 80a84b
	unsigned int total;
Packit Service 80a84b
};
Packit Service 80a84b
Packit Service 80a84b
struct hash {
Packit Service 80a84b
	unsigned int count;
Packit Service 80a84b
	unsigned int step;
Packit Service 80a84b
	unsigned int n_buckets;
Packit Service 80a84b
	void (*free_value)(void *value);
Packit Service 80a84b
	struct hash_bucket buckets[];
Packit Service 80a84b
};
Packit Service 80a84b
Packit Service 80a84b
static inline int hash_key_cmp(const char *key1, size_t size1,
Packit Service 80a84b
			       const char *key2, size_t size2)
Packit Service 80a84b
{
Packit Service 80a84b
	int rc;
Packit Service 80a84b
	size_t size;
Packit Service 80a84b
Packit Service 80a84b
	size = min(size1, size2);
Packit Service 80a84b
Packit Service 80a84b
	rc = memcmp(key1, key2, size);
Packit Service 80a84b
	if (rc != 0)
Packit Service 80a84b
		return rc;
Packit Service 80a84b
Packit Service 80a84b
	return (int)(size1 - size2);
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
struct hash *hash_new(unsigned int n_buckets,
Packit Service 80a84b
					void (*free_value)(void *value))
Packit Service 80a84b
{
Packit Service 80a84b
	struct hash *hash;
Packit Service 80a84b
Packit Service 80a84b
	n_buckets = ALIGN_POWER2(n_buckets);
Packit Service 80a84b
	hash = calloc(1, sizeof(struct hash) +
Packit Service 80a84b
		      n_buckets * sizeof(struct hash_bucket));
Packit Service 80a84b
	if (hash == NULL)
Packit Service 80a84b
		return NULL;
Packit Service 80a84b
	hash->n_buckets = n_buckets;
Packit Service 80a84b
	hash->free_value = free_value;
Packit Service 80a84b
	hash->step = n_buckets / 32;
Packit Service 80a84b
	if (hash->step == 0)
Packit Service 80a84b
		hash->step = 4;
Packit Service 80a84b
	else if (hash->step > 64)
Packit Service 80a84b
		hash->step = 64;
Packit Service 80a84b
	return hash;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
void hash_free(struct hash *hash)
Packit Service 80a84b
{
Packit Service 80a84b
	struct hash_bucket *bucket, *bucket_end;
Packit Service 80a84b
Packit Service 80a84b
	if (hash == NULL)
Packit Service 80a84b
		return;
Packit Service 80a84b
Packit Service 80a84b
	bucket = hash->buckets;
Packit Service 80a84b
	bucket_end = bucket + hash->n_buckets;
Packit Service 80a84b
	for (; bucket < bucket_end; bucket++) {
Packit Service 80a84b
		if (hash->free_value) {
Packit Service 80a84b
			struct hash_entry *entry, *entry_end;
Packit Service 80a84b
			entry = bucket->entries;
Packit Service 80a84b
			entry_end = entry + bucket->used;
Packit Service 80a84b
			for (; entry < entry_end; entry++)
Packit Service 80a84b
				hash->free_value((void *)entry->value);
Packit Service 80a84b
		}
Packit Service 80a84b
		free(bucket->entries);
Packit Service 80a84b
	}
Packit Service 80a84b
	free(hash);
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
static inline unsigned int hash_superfast(const char *key, unsigned int len)
Packit Service 80a84b
{
Packit Service 80a84b
	/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
Packit Service 80a84b
	 * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
Packit Service 80a84b
	 * EFL's eina and possible others.
Packit Service 80a84b
	 */
Packit Service 80a84b
	unsigned int tmp, hash = len, rem = len & 3;
Packit Service 80a84b
Packit Service 80a84b
	len /= 4;
Packit Service 80a84b
Packit Service 80a84b
	/* Main loop */
Packit Service 80a84b
	for (; len > 0; len--) {
Packit Service 80a84b
		hash += get_unaligned((uint16_t *) key);
Packit Service 80a84b
		tmp = (get_unaligned((uint16_t *)(key + 2)) << 11) ^ hash;
Packit Service 80a84b
		hash = (hash << 16) ^ tmp;
Packit Service 80a84b
		key += 4;
Packit Service 80a84b
		hash += hash >> 11;
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	/* Handle end cases */
Packit Service 80a84b
	switch (rem) {
Packit Service 80a84b
	case 3:
Packit Service 80a84b
		hash += get_unaligned((uint16_t *) key);
Packit Service 80a84b
		hash ^= hash << 16;
Packit Service 80a84b
		hash ^= key[2] << 18;
Packit Service 80a84b
		hash += hash >> 11;
Packit Service 80a84b
		break;
Packit Service 80a84b
Packit Service 80a84b
	case 2:
Packit Service 80a84b
		hash += get_unaligned((uint16_t *) key);
Packit Service 80a84b
		hash ^= hash << 11;
Packit Service 80a84b
		hash += hash >> 17;
Packit Service 80a84b
		break;
Packit Service 80a84b
Packit Service 80a84b
	case 1:
Packit Service 80a84b
		hash += *key;
Packit Service 80a84b
		hash ^= hash << 10;
Packit Service 80a84b
		hash += hash >> 1;
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	/* Force "avalanching" of final 127 bits */
Packit Service 80a84b
	hash ^= hash << 3;
Packit Service 80a84b
	hash += hash >> 5;
Packit Service 80a84b
	hash ^= hash << 4;
Packit Service 80a84b
	hash += hash >> 17;
Packit Service 80a84b
	hash ^= hash << 25;
Packit Service 80a84b
	hash += hash >> 6;
Packit Service 80a84b
Packit Service 80a84b
	return hash;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
/*
Packit Service 80a84b
 * add or replace key in hash map.
Packit Service 80a84b
 *
Packit Service 80a84b
 * none of key or value are copied, just references are remembered as is,
Packit Service 80a84b
 * make sure they are live while pair exists in hash!
Packit Service 80a84b
 */
Packit Service 80a84b
int hash_add_bin(struct hash *hash,
Packit Service 80a84b
		 const char *key, size_t keylen, const void *value)
Packit Service 80a84b
{
Packit Service 80a84b
	unsigned int hashval = hash_superfast(key, keylen);
Packit Service 80a84b
	unsigned int pos = hashval & (hash->n_buckets - 1);
Packit Service 80a84b
	struct hash_bucket *bucket = hash->buckets + pos;
Packit Service 80a84b
	struct hash_entry *entry, *entry_end;
Packit Service 80a84b
Packit Service 80a84b
	if (bucket->used + 1 >= bucket->total) {
Packit Service 80a84b
		unsigned new_total = bucket->total + hash->step;
Packit Service 80a84b
		size_t size = new_total * sizeof(struct hash_entry);
Packit Service 80a84b
		struct hash_entry *tmp = realloc(bucket->entries, size);
Packit Service 80a84b
		if (tmp == NULL)
Packit Service 80a84b
			return -errno;
Packit Service 80a84b
		bucket->entries = tmp;
Packit Service 80a84b
		bucket->total = new_total;
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	entry = bucket->entries;
Packit Service 80a84b
	entry_end = entry + bucket->used;
Packit Service 80a84b
	for (; entry < entry_end; entry++) {
Packit Service 80a84b
		int c = hash_key_cmp(key, keylen, entry->key, entry->keylen);
Packit Service 80a84b
		if (c == 0) {
Packit Service 80a84b
			if (hash->free_value)
Packit Service 80a84b
				hash->free_value((void *)entry->value);
Packit Service 80a84b
			entry->key = key;
Packit Service 80a84b
			entry->keylen = keylen;
Packit Service 80a84b
			entry->value = value;
Packit Service 80a84b
			return 0;
Packit Service 80a84b
		} else if (c < 0) {
Packit Service 80a84b
			memmove(entry + 1, entry,
Packit Service 80a84b
				(entry_end - entry) * sizeof(struct hash_entry));
Packit Service 80a84b
			break;
Packit Service 80a84b
		}
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	entry->key = key;
Packit Service 80a84b
	entry->keylen = keylen;
Packit Service 80a84b
	entry->value = value;
Packit Service 80a84b
	bucket->used++;
Packit Service 80a84b
	hash->count++;
Packit Service 80a84b
	return 0;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
/* similar to hash_add(), but fails if key already exists */
Packit Service 80a84b
int hash_add_unique_bin(struct hash *hash,
Packit Service 80a84b
			const char *key, size_t keylen, const void *value)
Packit Service 80a84b
{
Packit Service 80a84b
	unsigned int hashval = hash_superfast(key, keylen);
Packit Service 80a84b
	unsigned int pos = hashval & (hash->n_buckets - 1);
Packit Service 80a84b
	struct hash_bucket *bucket = hash->buckets + pos;
Packit Service 80a84b
	struct hash_entry *entry, *entry_end;
Packit Service 80a84b
Packit Service 80a84b
	if (bucket->used + 1 >= bucket->total) {
Packit Service 80a84b
		unsigned new_total = bucket->total + hash->step;
Packit Service 80a84b
		size_t size = new_total * sizeof(struct hash_entry);
Packit Service 80a84b
		struct hash_entry *tmp = realloc(bucket->entries, size);
Packit Service 80a84b
		if (tmp == NULL)
Packit Service 80a84b
			return -errno;
Packit Service 80a84b
		bucket->entries = tmp;
Packit Service 80a84b
		bucket->total = new_total;
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	entry = bucket->entries;
Packit Service 80a84b
	entry_end = entry + bucket->used;
Packit Service 80a84b
	for (; entry < entry_end; entry++) {
Packit Service 80a84b
		int c = hash_key_cmp(key, keylen, entry->key, entry->keylen);
Packit Service 80a84b
		if (c == 0)
Packit Service 80a84b
			return -EEXIST;
Packit Service 80a84b
		else if (c < 0) {
Packit Service 80a84b
			memmove(entry + 1, entry,
Packit Service 80a84b
				(entry_end - entry) * sizeof(struct hash_entry));
Packit Service 80a84b
			break;
Packit Service 80a84b
		}
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	entry->key = key;
Packit Service 80a84b
	entry->keylen = keylen;
Packit Service 80a84b
	entry->value = value;
Packit Service 80a84b
	bucket->used++;
Packit Service 80a84b
	hash->count++;
Packit Service 80a84b
	return 0;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
static int hash_entry_cmp(const void *pa, const void *pb)
Packit Service 80a84b
{
Packit Service 80a84b
	const struct hash_entry *a = pa;
Packit Service 80a84b
	const struct hash_entry *b = pb;
Packit Service 80a84b
	return hash_key_cmp(a->key, a->keylen, b->key, b->keylen);
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
void *hash_find_bin(const struct hash *hash, const char *key, size_t keylen)
Packit Service 80a84b
{
Packit Service 80a84b
	unsigned int hashval = hash_superfast(key, keylen);
Packit Service 80a84b
	unsigned int pos = hashval & (hash->n_buckets - 1);
Packit Service 80a84b
	const struct hash_bucket *bucket = hash->buckets + pos;
Packit Service 80a84b
	const struct hash_entry se = {
Packit Service 80a84b
		.key = key,
Packit Service 80a84b
		.keylen = keylen,
Packit Service 80a84b
		.value = NULL
Packit Service 80a84b
	};
Packit Service 80a84b
	const struct hash_entry *entry = bsearch(
Packit Service 80a84b
		&se, bucket->entries, bucket->used,
Packit Service 80a84b
		sizeof(struct hash_entry), hash_entry_cmp);
Packit Service 80a84b
	if (entry == NULL)
Packit Service 80a84b
		return NULL;
Packit Service 80a84b
	return (void *)entry->value;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
int hash_del_bin(struct hash *hash, const char *key, size_t keylen)
Packit Service 80a84b
{
Packit Service 80a84b
	unsigned int hashval = hash_superfast(key, keylen);
Packit Service 80a84b
	unsigned int pos = hashval & (hash->n_buckets - 1);
Packit Service 80a84b
	unsigned int steps_used, steps_total;
Packit Service 80a84b
	struct hash_bucket *bucket = hash->buckets + pos;
Packit Service 80a84b
	struct hash_entry *entry, *entry_end;
Packit Service 80a84b
	const struct hash_entry se = {
Packit Service 80a84b
		.key = key,
Packit Service 80a84b
		.keylen = keylen,
Packit Service 80a84b
		.value = NULL
Packit Service 80a84b
	};
Packit Service 80a84b
Packit Service 80a84b
	entry = bsearch(&se, bucket->entries, bucket->used,
Packit Service 80a84b
		sizeof(struct hash_entry), hash_entry_cmp);
Packit Service 80a84b
	if (entry == NULL)
Packit Service 80a84b
		return -ENOENT;
Packit Service 80a84b
Packit Service 80a84b
	if (hash->free_value)
Packit Service 80a84b
		hash->free_value((void *)entry->value);
Packit Service 80a84b
Packit Service 80a84b
	entry_end = bucket->entries + bucket->used;
Packit Service 80a84b
	memmove(entry, entry + 1,
Packit Service 80a84b
		(entry_end - entry) * sizeof(struct hash_entry));
Packit Service 80a84b
Packit Service 80a84b
	bucket->used--;
Packit Service 80a84b
	hash->count--;
Packit Service 80a84b
Packit Service 80a84b
	steps_used = bucket->used / hash->step;
Packit Service 80a84b
	steps_total = bucket->total / hash->step;
Packit Service 80a84b
	if (steps_used + 1 < steps_total) {
Packit Service 80a84b
		size_t size = (steps_used + 1) *
Packit Service 80a84b
			hash->step * sizeof(struct hash_entry);
Packit Service 80a84b
		struct hash_entry *tmp = realloc(bucket->entries, size);
Packit Service 80a84b
		if (tmp) {
Packit Service 80a84b
			bucket->entries = tmp;
Packit Service 80a84b
			bucket->total = (steps_used + 1) * hash->step;
Packit Service 80a84b
		}
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	return 0;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
unsigned int hash_get_count(const struct hash *hash)
Packit Service 80a84b
{
Packit Service 80a84b
	return hash->count;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
void hash_iter_init(const struct hash *hash, struct hash_iter *iter)
Packit Service 80a84b
{
Packit Service 80a84b
	iter->hash = hash;
Packit Service 80a84b
	iter->bucket = 0;
Packit Service 80a84b
	iter->entry = -1;
Packit Service 80a84b
}
Packit Service 80a84b
Packit Service 80a84b
bool hash_iter_next_bin(struct hash_iter *iter, const char **key,
Packit Service 80a84b
			size_t *keylen,
Packit Service 80a84b
			const void **value)
Packit Service 80a84b
{
Packit Service 80a84b
	const struct hash_bucket *b = iter->hash->buckets + iter->bucket;
Packit Service 80a84b
	const struct hash_entry *e;
Packit Service 80a84b
Packit Service 80a84b
	iter->entry++;
Packit Service 80a84b
Packit Service 80a84b
	if (iter->entry >= b->used) {
Packit Service 80a84b
		iter->entry = 0;
Packit Service 80a84b
Packit Service 80a84b
		for (iter->bucket++; iter->bucket < iter->hash->n_buckets;
Packit Service 80a84b
							iter->bucket++) {
Packit Service 80a84b
			b = iter->hash->buckets + iter->bucket;
Packit Service 80a84b
Packit Service 80a84b
			if (b->used > 0)
Packit Service 80a84b
				break;
Packit Service 80a84b
		}
Packit Service 80a84b
Packit Service 80a84b
		if (iter->bucket >= iter->hash->n_buckets)
Packit Service 80a84b
			return false;
Packit Service 80a84b
	}
Packit Service 80a84b
Packit Service 80a84b
	e = b->entries + iter->entry;
Packit Service 80a84b
Packit Service 80a84b
	if (value != NULL)
Packit Service 80a84b
		*value = e->value;
Packit Service 80a84b
	if (key != NULL)
Packit Service 80a84b
		*key = e->key;
Packit Service 80a84b
	if (keylen != NULL)
Packit Service 80a84b
		*keylen = e->keylen;
Packit Service 80a84b
Packit Service 80a84b
	return true;
Packit Service 80a84b
}