Add simple hash implementation
This commit is contained in:
parent
d753b8ca35
commit
7db08652cd
|
@ -31,6 +31,7 @@ libkmod_libkmod_la_SOURCES =\
|
|||
libkmod/macro.h \
|
||||
libkmod/libkmod.c \
|
||||
libkmod/libkmod-list.c \
|
||||
libkmod/libkmod-hash.c \
|
||||
libkmod/libkmod-loaded.c \
|
||||
libkmod/libkmod-config.c \
|
||||
libkmod/libkmod-util.c \
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* libkmod - interface to kernel module operations
|
||||
*
|
||||
* Copyright (C) 2011 ProFUSION embedded systems
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "libkmod.h"
|
||||
#include "libkmod-private.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct kmod_hash_entry {
|
||||
const char *key;
|
||||
const void *value;
|
||||
};
|
||||
|
||||
struct kmod_hash_bucket {
|
||||
struct kmod_hash_entry *entries;
|
||||
unsigned int used;
|
||||
unsigned int total;
|
||||
};
|
||||
|
||||
struct kmod_hash {
|
||||
unsigned int count;
|
||||
unsigned int step;
|
||||
unsigned int n_buckets;
|
||||
void (*free_value)(void *value);
|
||||
struct kmod_hash_bucket buckets[];
|
||||
};
|
||||
|
||||
struct kmod_hash *kmod_hash_new(unsigned int n_buckets, void (*free_value)(void *value))
|
||||
{
|
||||
struct kmod_hash *hash = calloc(1, sizeof(struct kmod_hash) +
|
||||
n_buckets * sizeof(struct kmod_hash_bucket));
|
||||
if (hash == NULL)
|
||||
return NULL;
|
||||
hash->n_buckets = n_buckets;
|
||||
hash->free_value = free_value;
|
||||
hash->step = n_buckets / 32;
|
||||
if (hash->step == 0)
|
||||
hash->step = 4;
|
||||
else if (hash->step > 64)
|
||||
hash->step = 64;
|
||||
return hash;
|
||||
}
|
||||
|
||||
void kmod_hash_free(struct kmod_hash *hash)
|
||||
{
|
||||
struct kmod_hash_bucket *bucket, *bucket_end;
|
||||
bucket = hash->buckets;
|
||||
bucket_end = bucket + hash->n_buckets;
|
||||
for (; bucket < bucket_end; bucket++) {
|
||||
if (hash->free_value) {
|
||||
struct kmod_hash_entry *entry, *entry_end;
|
||||
entry = bucket->entries;
|
||||
entry_end = entry + bucket->used;
|
||||
for (; entry < entry_end; entry++)
|
||||
hash->free_value((void *)entry->value);
|
||||
}
|
||||
free(bucket->entries);
|
||||
}
|
||||
free(hash);
|
||||
}
|
||||
|
||||
static inline unsigned int hash_superfast(const char *key, unsigned int len)
|
||||
{
|
||||
/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
|
||||
* used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
|
||||
* EFL's eina and possible others.
|
||||
*/
|
||||
unsigned int tmp, hash = len, rem = len & 3;
|
||||
const unsigned short *itr = (const unsigned short *)key;
|
||||
|
||||
len /= 4;
|
||||
|
||||
/* Main loop */
|
||||
for (; len > 0; len--) {
|
||||
hash += itr[0];
|
||||
tmp = (itr[1] << 11) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
itr += 2;
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3:
|
||||
hash += *itr;
|
||||
hash ^= hash << 16;
|
||||
hash ^= key[2] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
hash += *itr;
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
hash += *(const char *)itr;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* add or replace key in hash map.
|
||||
*
|
||||
* none of key or value are copied, just references are remembered as is,
|
||||
* make sure they are live while pair exists in hash!
|
||||
*/
|
||||
int kmod_hash_add(struct kmod_hash *hash, const char *key, const void *value)
|
||||
{
|
||||
unsigned int keylen = strlen(key);
|
||||
unsigned int hashval = hash_superfast(key, keylen);
|
||||
unsigned int pos = hashval % hash->n_buckets;
|
||||
struct kmod_hash_bucket *bucket = hash->buckets + pos;
|
||||
struct kmod_hash_entry *entry, *entry_end;
|
||||
|
||||
if (bucket->used + 1 >= bucket->total) {
|
||||
unsigned new_total = bucket->total + hash->step;
|
||||
size_t size = new_total * sizeof(struct kmod_hash_entry);
|
||||
struct kmod_hash_entry *tmp = realloc(bucket->entries, size);
|
||||
if (tmp == NULL)
|
||||
return -errno;
|
||||
bucket->entries = tmp;
|
||||
bucket->total = new_total;
|
||||
}
|
||||
|
||||
entry = bucket->entries;
|
||||
entry_end = entry + bucket->used;
|
||||
for (; entry < entry_end; entry++) {
|
||||
int c = strcmp(key, entry->key);
|
||||
if (c == 0) {
|
||||
hash->free_value((void *)entry->value);
|
||||
entry->value = value;
|
||||
return 0;
|
||||
} else if (c < 0) {
|
||||
memmove(entry + 1, entry,
|
||||
(entry_end - entry) * sizeof(struct kmod_hash_entry));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
bucket->used++;
|
||||
hash->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kmod_hash_entry_cmp(const void *pa, const void *pb)
|
||||
{
|
||||
const struct kmod_hash_entry *a = pa;
|
||||
const struct kmod_hash_entry *b = pb;
|
||||
return strcmp(a->key, b->key);
|
||||
}
|
||||
|
||||
void *kmod_hash_find(const struct kmod_hash *hash, const char *key)
|
||||
{
|
||||
unsigned int keylen = strlen(key);
|
||||
unsigned int hashval = hash_superfast(key, keylen);
|
||||
unsigned int pos = hashval % hash->n_buckets;
|
||||
const struct kmod_hash_bucket *bucket = hash->buckets + pos;
|
||||
const struct kmod_hash_entry se = {
|
||||
.key = key,
|
||||
.value = NULL
|
||||
};
|
||||
const struct kmod_hash_entry *entry = bsearch(
|
||||
&se, bucket->entries, bucket->used,
|
||||
sizeof(struct kmod_hash_entry), kmod_hash_entry_cmp);
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
return (void *)entry->value;
|
||||
}
|
||||
|
||||
int kmod_hash_del(struct kmod_hash *hash, const char *key)
|
||||
{
|
||||
unsigned int keylen = strlen(key);
|
||||
unsigned int hashval = hash_superfast(key, keylen);
|
||||
unsigned int pos = hashval % hash->n_buckets;
|
||||
unsigned int steps_used, steps_total;
|
||||
struct kmod_hash_bucket *bucket = hash->buckets + pos;
|
||||
struct kmod_hash_entry *entry, *entry_end;
|
||||
const struct kmod_hash_entry se = {
|
||||
.key = key,
|
||||
.value = NULL
|
||||
};
|
||||
|
||||
entry = bsearch(&se, bucket->entries, bucket->used,
|
||||
sizeof(struct kmod_hash_entry), kmod_hash_entry_cmp);
|
||||
if (entry == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
entry_end = bucket->entries + bucket->used;
|
||||
memmove(entry, entry + 1,
|
||||
(entry_end - entry) * sizeof(struct kmod_hash_entry));
|
||||
|
||||
bucket->used--;
|
||||
hash->count--;
|
||||
|
||||
steps_used = bucket->used / hash->step;
|
||||
steps_total = bucket->total / hash->step;
|
||||
if (steps_used + 1 < steps_total) {
|
||||
size_t size = (steps_used + 1) *
|
||||
hash->step * sizeof(struct kmod_hash_entry);
|
||||
struct kmod_hash_entry *tmp = realloc(bucket->entries, size);
|
||||
if (tmp) {
|
||||
bucket->entries = tmp;
|
||||
bucket->total = (steps_used + 1) * hash->step;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -81,6 +81,15 @@ const char *kmod_alias_get_modname(const struct kmod_list *l) __attribute__((non
|
|||
/* libkmod-module.c */
|
||||
int kmod_module_parse_dep(struct kmod_module *mod, char *line) __attribute__((nonnull(1, 2)));
|
||||
|
||||
/* libkmod-hash.c */
|
||||
struct kmod_hash;
|
||||
struct kmod_hash *kmod_hash_new(unsigned int n_buckets, void (*free_value)(void *value));
|
||||
void kmod_hash_free(struct kmod_hash *hash);
|
||||
int kmod_hash_add(struct kmod_hash *hash, const char *key, const void *value);
|
||||
int kmod_hash_del(struct kmod_hash *hash, const char *key);
|
||||
void *kmod_hash_find(const struct kmod_hash *hash, const char *key);
|
||||
|
||||
|
||||
/* util functions */
|
||||
char *getline_wrapped(FILE *fp, unsigned int *linenum) __attribute__((nonnull(1)));
|
||||
char *underscores(struct kmod_ctx *ctx, char *s) __attribute__((nonnull(1, 2)));
|
||||
|
|
Loading…
Reference in New Issue