Files
ldb/kvs_hash_bin.c
2026-03-05 08:45:23 +00:00

681 lines
16 KiB
C
Executable File

#include "kvstore.h"
#include "kvs_rw_tools.h"
#include "memory/alloc_dispatch.h"
#include "diskuring/diskuring.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#define KVS_HASH_INITIAL_GLOBAL_DEPTH 1u
#define KVS_HASH_BUCKET_SPLIT_THRESHOLD 64u
#define KVS_HASH_MAX_GLOBAL_DEPTH 31u
kvs_hash_t global_hash;
static uint32_t _hash_u32(const void *key, uint32_t key_len) {
const uint8_t *p = (const uint8_t *)key;
uint32_t hash = 2166136261u;
for (uint32_t i = 0; i < key_len; i++) {
hash ^= p[i];
hash *= 16777619u;
}
return hash;
}
static inline uint32_t _dir_index(uint32_t hashv, uint32_t global_depth) {
if (global_depth == 0) {
return 0;
}
return hashv & ((1u << global_depth) - 1u);
}
static hashbucket_t *_bucket_create(uint32_t local_depth) {
hashbucket_t *bucket = (hashbucket_t *)kvs_malloc(sizeof(hashbucket_t));
if (!bucket) {
return NULL;
}
bucket->head = NULL;
bucket->local_depth = local_depth;
bucket->item_count = 0;
bucket->next_all = NULL;
return bucket;
}
static void _bucket_list_push(kvs_hash_t *hash, hashbucket_t *bucket) {
bucket->next_all = hash->bucket_list;
hash->bucket_list = bucket;
}
static inline uint8_t *_get_node_key(const hashnode_t *node) {
return (uint8_t *)node + sizeof(hashnode_t);
}
static inline uint8_t *_get_node_value(const hashnode_t *node) {
return (uint8_t *)node + sizeof(hashnode_t) + node->key_len;
}
static int _key_equal(const hashnode_t *node, const void *key, uint32_t key_len) {
if (!node || !key) {
return 0;
}
if (node->key_len != key_len) {
return 0;
}
return memcmp(_get_node_key(node), key, key_len) == 0;
}
static hashnode_t *_create_node(const void *key, uint32_t key_len,
const void *value, uint32_t value_len) {
size_t total_size = sizeof(hashnode_t) + key_len + value_len;
hashnode_t *node;
uint8_t *data_ptr;
if (!key || key_len == 0) {
return NULL;
}
node = (hashnode_t *)kvs_malloc(total_size);
if (!node) {
return NULL;
}
memset(node, 0, sizeof(hashnode_t));
node->key_len = key_len;
node->value_len = value_len;
node->next = NULL;
data_ptr = (uint8_t *)node + sizeof(hashnode_t);
memcpy(data_ptr, key, key_len);
if (value_len > 0 && value) {
memcpy(data_ptr + key_len, value, value_len);
}
return node;
}
static hashnode_t *_find_node(hashbucket_t *bucket,
const void *key, uint32_t key_len,
hashnode_t ***out_indirect) {
hashnode_t **indirect;
if (out_indirect) {
*out_indirect = NULL;
}
if (!bucket || !key || key_len == 0) {
return NULL;
}
indirect = &bucket->head;
while (*indirect) {
if (_key_equal(*indirect, key, key_len)) {
if (out_indirect) {
*out_indirect = indirect;
}
return *indirect;
}
indirect = &(*indirect)->next;
}
return NULL;
}
static int _update_node_value(hashnode_t **node_ptr, const void *value, uint32_t value_len) {
hashnode_t *old_node;
hashnode_t *new_node;
if (!node_ptr || !*node_ptr) {
return -1;
}
if (value_len > 0 && !value) {
return -1;
}
old_node = *node_ptr;
if (old_node->value_len == value_len) {
if (value_len > 0) {
memcpy(_get_node_value(old_node), value, value_len);
}
return 0;
}
new_node = _create_node(_get_node_key(old_node), old_node->key_len, value, value_len);
if (!new_node) {
return -1;
}
new_node->next = old_node->next;
*node_ptr = new_node;
kvs_free(old_node);
return 0;
}
static int _double_directory(kvs_hash_t *hash) {
uint32_t old_size;
uint32_t new_size;
hashbucket_t **new_dir;
uint32_t i;
if (!hash || !hash->directory) {
return -1;
}
if (hash->global_depth >= KVS_HASH_MAX_GLOBAL_DEPTH) {
return 1;
}
old_size = hash->dir_size;
if (old_size == 0 || old_size > UINT32_MAX / 2u) {
return 1;
}
new_size = old_size << 1;
new_dir = (hashbucket_t **)kvs_malloc(sizeof(hashbucket_t *) * new_size);
if (!new_dir) {
return 1;
}
for (i = 0; i < old_size; i++) {
new_dir[i] = hash->directory[i];
new_dir[i + old_size] = hash->directory[i];
}
kvs_free(hash->directory);
hash->directory = new_dir;
hash->dir_size = new_size;
hash->global_depth++;
return 0;
}
static int _split_bucket(kvs_hash_t *hash, uint32_t dir_idx) {
hashbucket_t *old_bucket;
hashbucket_t *new_bucket;
hashnode_t *node;
uint32_t split_bit;
uint32_t i;
if (!hash || !hash->directory || dir_idx >= hash->dir_size) {
return -1;
}
old_bucket = hash->directory[dir_idx];
if (!old_bucket) {
return -1;
}
if (old_bucket->local_depth >= KVS_HASH_MAX_GLOBAL_DEPTH) {
return 1;
}
if (old_bucket->local_depth == hash->global_depth) {
int rc = _double_directory(hash);
if (rc != 0) {
return rc;
}
}
new_bucket = _bucket_create(old_bucket->local_depth + 1);
if (!new_bucket) {
return -1;
}
_bucket_list_push(hash, new_bucket);
old_bucket->local_depth++;
split_bit = 1u << (old_bucket->local_depth - 1u);
for (i = 0; i < hash->dir_size; i++) {
if (hash->directory[i] == old_bucket && (i & split_bit)) {
hash->directory[i] = new_bucket;
}
}
node = old_bucket->head;
old_bucket->head = NULL;
old_bucket->item_count = 0;
while (node) {
hashnode_t *next = node->next;
uint32_t idx = _dir_index(_hash_u32(_get_node_key(node), node->key_len), hash->global_depth);
hashbucket_t *target = hash->directory[idx];
node->next = target->head;
target->head = node;
target->item_count++;
node = next;
}
return 0;
}
int kvs_hash_create(kvs_hash_t *hash) {
uint32_t init_depth;
uint32_t i;
hashbucket_t *initial_bucket;
if (!hash) {
return -1;
}
memset(hash, 0, sizeof(*hash));
init_depth = KVS_HASH_INITIAL_GLOBAL_DEPTH;
if (init_depth > KVS_HASH_MAX_GLOBAL_DEPTH) {
init_depth = KVS_HASH_MAX_GLOBAL_DEPTH;
}
hash->global_depth = init_depth;
hash->dir_size = 1u << init_depth;
if (hash->dir_size == 0) {
return -1;
}
hash->directory = (hashbucket_t **)kvs_malloc(sizeof(hashbucket_t *) * hash->dir_size);
if (!hash->directory) {
return -1;
}
memset(hash->directory, 0, sizeof(hashbucket_t *) * hash->dir_size);
initial_bucket = _bucket_create(0);
if (!initial_bucket) {
kvs_free(hash->directory);
hash->directory = NULL;
hash->dir_size = 0;
return -1;
}
_bucket_list_push(hash, initial_bucket);
for (i = 0; i < hash->dir_size; i++) {
hash->directory[i] = initial_bucket;
}
hash->count = 0;
return 0;
}
void kvs_hash_destroy(kvs_hash_t *hash) {
hashbucket_t *bucket;
if (!hash) {
return;
}
bucket = hash->bucket_list;
while (bucket) {
hashbucket_t *next_bucket = bucket->next_all;
hashnode_t *node = bucket->head;
while (node) {
hashnode_t *next = node->next;
kvs_free(node);
node = next;
}
kvs_free(bucket);
bucket = next_bucket;
}
kvs_free(hash->directory);
memset(hash, 0, sizeof(*hash));
}
int kvs_hash_set_bin(kvs_hash_t *hash, const void *key, uint32_t key_len,
const void *value, uint32_t value_len) {
uint32_t hashv;
uint32_t idx;
hashbucket_t *bucket;
hashnode_t **indirect = NULL;
hashnode_t *found;
hashnode_t *new_node;
if (!hash || !hash->directory || !key || key_len == 0) {
return -1;
}
if (value_len > 0 && !value) {
return -1;
}
hashv = _hash_u32(key, key_len);
idx = _dir_index(hashv, hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return -1;
}
found = _find_node(bucket, key, key_len, &indirect);
if (found) {
return (_update_node_value(indirect, value, value_len) == 0) ? 0 : -2;
}
while (bucket->item_count >= KVS_HASH_BUCKET_SPLIT_THRESHOLD) {
int split_rc = _split_bucket(hash, idx);
if (split_rc < 0) {
return -2;
}
if (split_rc > 0) {
break;
}
idx = _dir_index(hashv, hash->global_depth);
bucket = hash->directory[idx];
}
new_node = _create_node(key, key_len, value, value_len);
if (!new_node) {
return -2;
}
new_node->next = bucket->head;
bucket->head = new_node;
bucket->item_count++;
hash->count++;
return 0;
}
void *kvs_hash_get_bin(kvs_hash_t *hash, const void *key, uint32_t key_len,
uint32_t *out_value_len) {
uint32_t idx;
hashbucket_t *bucket;
hashnode_t *node;
if (!out_value_len) {
return NULL;
}
*out_value_len = 0;
if (!hash || !hash->directory || !key || key_len == 0) {
return NULL;
}
idx = _dir_index(_hash_u32(key, key_len), hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return NULL;
}
node = _find_node(bucket, key, key_len, NULL);
if (!node) {
return NULL;
}
*out_value_len = node->value_len;
return (node->value_len > 0) ? _get_node_value(node) : NULL;
}
int kvs_hash_get_copy_bin(kvs_hash_t *hash, const void *key, uint32_t key_len,
void **out_buf, uint32_t *out_len) {
uint32_t idx;
hashbucket_t *bucket;
hashnode_t *node;
void *copy;
if (!out_buf || !out_len) {
return -1;
}
*out_buf = NULL;
*out_len = 0;
if (!hash || !hash->directory || !key || key_len == 0) {
return -1;
}
idx = _dir_index(_hash_u32(key, key_len), hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return -1;
}
node = _find_node(bucket, key, key_len, NULL);
if (!node) {
return 1;
}
*out_len = node->value_len;
if (node->value_len == 0) {
return 0;
}
copy = kvs_malloc(node->value_len);
if (!copy) {
*out_len = 0;
return -2;
}
memcpy(copy, _get_node_value(node), node->value_len);
*out_buf = copy;
return 0;
}
int kvs_hash_mod_bin(kvs_hash_t *hash, const void *key, uint32_t key_len,
const void *value, uint32_t value_len) {
uint32_t idx;
hashbucket_t *bucket;
hashnode_t **indirect = NULL;
hashnode_t *node;
if (!hash || !hash->directory || !key || key_len == 0) {
return -1;
}
if (value_len > 0 && !value) {
return -1;
}
idx = _dir_index(_hash_u32(key, key_len), hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return -1;
}
node = _find_node(bucket, key, key_len, &indirect);
if (!node) {
return 1;
}
return (_update_node_value(indirect, value, value_len) == 0) ? 0 : -2;
}
int kvs_hash_del_bin(kvs_hash_t *hash, const void *key, uint32_t key_len) {
uint32_t idx;
hashbucket_t *bucket;
hashnode_t **indirect = NULL;
hashnode_t *node;
if (!hash || !hash->directory || !key || key_len == 0) {
return -1;
}
idx = _dir_index(_hash_u32(key, key_len), hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return -1;
}
node = _find_node(bucket, key, key_len, &indirect);
if (!node) {
return 1;
}
*indirect = node->next;
kvs_free(node);
bucket->item_count--;
hash->count--;
return 0;
}
int kvs_hash_exist_bin(kvs_hash_t *hash, const void *key, uint32_t key_len) {
uint32_t idx;
hashbucket_t *bucket;
if (!hash || !hash->directory || !key || key_len == 0) {
return -1;
}
idx = _dir_index(_hash_u32(key, key_len), hash->global_depth);
bucket = hash->directory[idx];
if (!bucket) {
return 1;
}
return _find_node(bucket, key, key_len, NULL) ? 0 : 1;
}
int kvs_hash_count(kvs_hash_t *hash) {
return hash ? hash->count : 0;
}
int kvs_hash_save(iouring_ctx_t *uring, kvs_hash_t *inst, const char *filename) {
int fd;
off_t current_off = 0;
int rc = 0;
hashbucket_t *bucket;
if (!uring || !inst || !filename) {
return -1;
}
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
return -2;
}
for (bucket = inst->bucket_list; bucket != NULL; bucket = bucket->next_all) {
hashnode_t *n = bucket->head;
while (n != NULL) {
uint32_t klen = htonl(n->key_len);
uint32_t vlen = htonl(n->value_len);
uint8_t *key_ptr = _get_node_key(n);
uint8_t *val_ptr = (n->value_len > 0) ? _get_node_value(n) : NULL;
void *bufs[4];
size_t lens[4];
int count = 0;
size_t total = 0;
task_t *t;
bufs[count] = &klen;
lens[count++] = sizeof(klen);
bufs[count] = &vlen;
lens[count++] = sizeof(vlen);
if (n->key_len > 0) {
bufs[count] = key_ptr;
lens[count++] = n->key_len;
}
if (n->value_len > 0) {
bufs[count] = val_ptr;
lens[count++] = n->value_len;
}
for (int j = 0; j < count; j++) {
total += lens[j];
}
t = submit_write(uring, fd, bufs, lens, count, current_off);
if (!t) {
rc = -3;
goto done;
}
cleanup_finished_iouring_tasks(uring);
current_off += (off_t)total;
n = n->next;
}
}
done:
while (!uring_task_complete(uring)) {
usleep(1000);
cleanup_finished_iouring_tasks(uring);
}
close(fd);
return rc;
}
int kvs_hash_load(kvs_hash_t *inst, const char *filename) {
int fd;
int rc = 0;
if (!inst || !filename) {
return -1;
}
if (!inst->directory || inst->dir_size == 0) {
return -1;
}
fd = open(filename, O_RDONLY);
if (fd < 0) {
return -2;
}
while (1) {
uint32_t klen_n = 0;
uint32_t vlen_n = 0;
uint32_t klen;
uint32_t vlen;
uint8_t *keybuf = NULL;
uint8_t *valbuf = NULL;
int rr;
rr = read_full(fd, &klen_n, sizeof(klen_n));
if (rr == 0) {
rc = 0;
break;
}
if (rr < 0) {
rc = -3;
break;
}
rr = read_full(fd, &vlen_n, sizeof(vlen_n));
if (rr <= 0) {
rc = -3;
break;
}
klen = ntohl(klen_n);
vlen = ntohl(vlen_n);
if (klen == 0) {
rc = -3;
break;
}
keybuf = (uint8_t *)kvs_malloc((size_t)klen);
if (!keybuf) {
rc = -4;
break;
}
rr = read_full(fd, keybuf, (size_t)klen);
if (rr <= 0) {
kvs_free(keybuf);
rc = -3;
break;
}
if (vlen > 0) {
valbuf = (uint8_t *)kvs_malloc((size_t)vlen);
if (!valbuf) {
kvs_free(keybuf);
rc = -4;
break;
}
rr = read_full(fd, valbuf, (size_t)vlen);
if (rr <= 0) {
kvs_free(valbuf);
kvs_free(keybuf);
rc = -3;
break;
}
}
rr = kvs_hash_set_bin(inst, keybuf, klen, valbuf, vlen);
kvs_free(keybuf);
if (valbuf) {
kvs_free(valbuf);
}
if (rr < 0) {
rc = -5;
break;
}
}
close(fd);
return rc;
}