|
|
|
@ -6,13 +6,12 @@
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include "lock.h"
|
|
|
|
|
#include "xmalloc.h"
|
|
|
|
|
|
|
|
|
|
/* Simple K-V store based on The Practice of Programming by Kernighan and Pike */
|
|
|
|
|
|
|
|
|
|
/* Bucket count is sized to be a prime that is approximately 20% larger than the desired capacity (6k keys) */
|
|
|
|
|
#define MAP_BUCKET_COUNT 7907
|
|
|
|
|
#define MAP_LOCK_STRIPES 17
|
|
|
|
|
|
|
|
|
|
#define MAP_HASH jenkins_hash
|
|
|
|
|
|
|
|
|
|
struct map_node {
|
|
|
|
@ -24,16 +23,22 @@ struct map_node {
|
|
|
|
|
uint32_t hash;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct map_bucket {
|
|
|
|
|
lock_t lock;
|
|
|
|
|
struct map_node *head;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct map {
|
|
|
|
|
struct map_node *buckets[MAP_BUCKET_COUNT];
|
|
|
|
|
lock_t locks[MAP_LOCK_STRIPES];
|
|
|
|
|
struct map_bucket buckets[MAP_BUCKET_COUNT];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
map_init(struct map *restrict map)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < MAP_BUCKET_COUNT; i++) { map->buckets[i] = NULL; }
|
|
|
|
|
for (int i = 0; i < MAP_LOCK_STRIPES; i++) { LOCK_INIT(&map->locks[i]); }
|
|
|
|
|
for (int i = 0; i < MAP_BUCKET_COUNT; i++) {
|
|
|
|
|
map->buckets[i].head = NULL;
|
|
|
|
|
LOCK_INIT(&map->buckets[i].lock);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* See https://en.wikipedia.org/wiki/Jenkins_hash_function */
|
|
|
|
@ -59,8 +64,11 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
|
|
|
|
|
uint8_t *value = NULL;
|
|
|
|
|
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) {
|
|
|
|
|
|
|
|
|
|
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
|
|
|
|
|
|
|
|
|
LOCK_LOCK(&bucket->lock);
|
|
|
|
|
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
|
|
|
|
if (node->hash == hash) {
|
|
|
|
|
value = node->value;
|
|
|
|
|
*ret_value_len = node->value_len;
|
|
|
|
@ -71,7 +79,7 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
|
|
|
|
|
if (value == NULL) *ret_value_len = 0;
|
|
|
|
|
|
|
|
|
|
DONE:
|
|
|
|
|
LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
LOCK_UNLOCK(&bucket->lock);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -80,33 +88,30 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_
|
|
|
|
|
{
|
|
|
|
|
bool did_set = false;
|
|
|
|
|
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) {
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
|
|
|
|
LOCK_LOCK(&bucket->lock);
|
|
|
|
|
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
|
|
|
|
if (node->hash == hash) goto DONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct map_node *new_node = (struct map_node *)malloc(sizeof(struct map_node));
|
|
|
|
|
|
|
|
|
|
*(new_node) = (struct map_node){ .hash = hash,
|
|
|
|
|
.key = malloc(key_len),
|
|
|
|
|
.key_len = key_len,
|
|
|
|
|
.value = malloc(value_len),
|
|
|
|
|
.value_len = value_len,
|
|
|
|
|
.next = map->buckets[hash % MAP_BUCKET_COUNT] };
|
|
|
|
|
|
|
|
|
|
assert(new_node->key);
|
|
|
|
|
assert(new_node->value);
|
|
|
|
|
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
|
|
|
|
|
*(new_node) = (struct map_node){ .hash = hash,
|
|
|
|
|
.key = xmalloc(key_len),
|
|
|
|
|
.key_len = key_len,
|
|
|
|
|
.value = xmalloc(value_len),
|
|
|
|
|
.value_len = value_len,
|
|
|
|
|
.next = bucket->head };
|
|
|
|
|
|
|
|
|
|
// Copy Key and Value
|
|
|
|
|
memcpy(new_node->key, key, key_len);
|
|
|
|
|
memcpy(new_node->value, value, value_len);
|
|
|
|
|
|
|
|
|
|
map->buckets[hash % MAP_BUCKET_COUNT] = new_node;
|
|
|
|
|
did_set = true;
|
|
|
|
|
bucket->head = new_node;
|
|
|
|
|
did_set = true;
|
|
|
|
|
|
|
|
|
|
DONE:
|
|
|
|
|
LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
LOCK_UNLOCK(&bucket->lock);
|
|
|
|
|
return did_set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -118,12 +123,13 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
|
|
|
|
|
{
|
|
|
|
|
bool did_delete = false;
|
|
|
|
|
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
|
|
|
|
LOCK_LOCK(&bucket->lock);
|
|
|
|
|
|
|
|
|
|
struct map_node *prev = map->buckets[hash % MAP_BUCKET_COUNT];
|
|
|
|
|
if (prev->hash == hash) {
|
|
|
|
|
map->buckets[hash % MAP_BUCKET_COUNT] = prev->next;
|
|
|
|
|
struct map_node *prev = bucket->head;
|
|
|
|
|
if (prev != NULL && prev->hash == hash) {
|
|
|
|
|
bucket->head = prev->next;
|
|
|
|
|
free(prev->key);
|
|
|
|
|
free(prev->value);
|
|
|
|
|
free(prev);
|
|
|
|
@ -141,33 +147,35 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DONE:
|
|
|
|
|
LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
LOCK_UNLOCK(&bucket->lock);
|
|
|
|
|
return did_delete;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_t value_len)
|
|
|
|
|
{
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
LOCK_LOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
for (struct map_node *node = map->buckets[hash % MAP_BUCKET_COUNT]; node != NULL; node = node->next) {
|
|
|
|
|
uint32_t hash = MAP_HASH(key, key_len);
|
|
|
|
|
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
|
|
|
|
LOCK_LOCK(&bucket->lock);
|
|
|
|
|
|
|
|
|
|
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
|
|
|
|
if (node->hash == hash) {
|
|
|
|
|
node->value_len = value_len;
|
|
|
|
|
node->value = realloc(node->value, value_len);
|
|
|
|
|
assert(node->value);
|
|
|
|
|
memcpy(node->value, value, value_len);
|
|
|
|
|
goto DONE;
|
|
|
|
|
}
|
|
|
|
|
goto DONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct map_node *new_node = (struct map_node *)malloc(sizeof(struct map_node));
|
|
|
|
|
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
|
|
|
|
|
|
|
|
|
|
*(new_node) = (struct map_node){ .hash = hash,
|
|
|
|
|
.key = malloc(key_len),
|
|
|
|
|
.key = xmalloc(key_len),
|
|
|
|
|
.key_len = key_len,
|
|
|
|
|
.value = malloc(value_len),
|
|
|
|
|
.value = xmalloc(value_len),
|
|
|
|
|
.value_len = value_len,
|
|
|
|
|
.next = map->buckets[hash % MAP_BUCKET_COUNT] };
|
|
|
|
|
.next = bucket->head };
|
|
|
|
|
|
|
|
|
|
assert(new_node->key);
|
|
|
|
|
assert(new_node->value);
|
|
|
|
@ -176,8 +184,8 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
|
|
|
|
|
memcpy(new_node->key, key, key_len);
|
|
|
|
|
memcpy(new_node->value, value, value_len);
|
|
|
|
|
|
|
|
|
|
map->buckets[hash % MAP_BUCKET_COUNT] = new_node;
|
|
|
|
|
bucket->head = new_node;
|
|
|
|
|
|
|
|
|
|
DONE:
|
|
|
|
|
LOCK_UNLOCK(&map->locks[hash % MAP_LOCK_STRIPES]);
|
|
|
|
|
LOCK_UNLOCK(&bucket->lock);
|
|
|
|
|
}
|
|
|
|
|