nostrdb: pull latest, adding flatcc and lmdb
This commit is contained in:
248
nostrdb/flatcc/refmap.c
Normal file
248
nostrdb/flatcc/refmap.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Optional file that can be included in runtime library to support DAG
|
||||
* cloning with the builder and may also be used for custom purposes
|
||||
* standalone. See also comments in `flatcc/flatcc_builder.h`.
|
||||
*
|
||||
* Note that dynamic construction takes place and that large offset
|
||||
* vectors might consume significant space if there are not many shared
|
||||
* references. In the basic use case no allocation takes place because a
|
||||
* few references can be held using only a small stack allocated hash
|
||||
* table.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "flatcc_rtconfig.h"
|
||||
#include "flatcc_refmap.h"
|
||||
#include "flatcc_alloc.h"
|
||||
#include "flatcc_assert.h"
|
||||
|
||||
#define _flatcc_refmap_calloc FLATCC_CALLOC
|
||||
#define _flatcc_refmap_free FLATCC_FREE
|
||||
|
||||
/* Can be used as a primitive defense against collision attacks. */
|
||||
#ifdef FLATCC_HASH_SEED
|
||||
#define _flatcc_refmap_seed FLATCC_HASH_SEED
|
||||
#else
|
||||
#define _flatcc_refmap_seed 0x2f693b52
|
||||
#endif
|
||||
|
||||
static inline size_t _flatcc_refmap_above_load_factor(size_t count, size_t buckets)
|
||||
{
|
||||
static const size_t d = 256;
|
||||
static const size_t n = (size_t)((FLATCC_REFMAP_LOAD_FACTOR) * 256.0f);
|
||||
|
||||
return count >= buckets * n / d;
|
||||
}
|
||||
|
||||
#define _flatcc_refmap_probe(k, i, N) ((k + i) & N)
|
||||
|
||||
void flatcc_refmap_clear(flatcc_refmap_t *refmap)
|
||||
{
|
||||
if (refmap->table && refmap->table != refmap->min_table) {
|
||||
_flatcc_refmap_free(refmap->table);
|
||||
}
|
||||
flatcc_refmap_init(refmap);
|
||||
}
|
||||
|
||||
static inline size_t _flatcc_refmap_hash(const void *src)
|
||||
{
|
||||
/* MurmurHash3 64-bit finalizer */
|
||||
uint64_t x;
|
||||
|
||||
x = (uint64_t)((size_t)src) ^ _flatcc_refmap_seed;
|
||||
|
||||
x ^= x >> 33;
|
||||
x *= 0xff51afd7ed558ccdULL;
|
||||
x ^= x >> 33;
|
||||
x *= 0xc4ceb9fe1a85ec53ULL;
|
||||
x ^= x >> 33;
|
||||
return (size_t)x;
|
||||
}
|
||||
|
||||
void flatcc_refmap_reset(flatcc_refmap_t *refmap)
|
||||
{
|
||||
if (refmap->count) {
|
||||
memset(refmap->table, 0, sizeof(refmap->table[0]) * refmap->buckets);
|
||||
}
|
||||
refmap->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Technically resize also supports shrinking which may be useful for
|
||||
* adapations, but the current hash table never deletes individual items.
|
||||
*/
|
||||
int flatcc_refmap_resize(flatcc_refmap_t *refmap, size_t count)
|
||||
{
|
||||
const size_t min_buckets = sizeof(refmap->min_table) / sizeof(refmap->min_table[0]);
|
||||
|
||||
size_t i;
|
||||
size_t buckets;
|
||||
size_t buckets_old;
|
||||
struct flatcc_refmap_item *T_old;
|
||||
|
||||
if (count < refmap->count) {
|
||||
count = refmap->count;
|
||||
}
|
||||
buckets = min_buckets;
|
||||
|
||||
while (_flatcc_refmap_above_load_factor(count, buckets)) {
|
||||
buckets *= 2;
|
||||
}
|
||||
if (refmap->buckets == buckets) {
|
||||
return 0;
|
||||
}
|
||||
T_old = refmap->table;
|
||||
buckets_old = refmap->buckets;
|
||||
if (buckets == min_buckets) {
|
||||
memset(refmap->min_table, 0, sizeof(refmap->min_table));
|
||||
refmap->table = refmap->min_table;
|
||||
} else {
|
||||
refmap->table = _flatcc_refmap_calloc(buckets, sizeof(refmap->table[0]));
|
||||
if (refmap->table == 0) {
|
||||
refmap->table = T_old;
|
||||
FLATCC_ASSERT(0); /* out of memory */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
refmap->buckets = buckets;
|
||||
refmap->count = 0;
|
||||
for (i = 0; i < buckets_old; ++i) {
|
||||
if (T_old[i].src) {
|
||||
flatcc_refmap_insert(refmap, T_old[i].src, T_old[i].ref);
|
||||
}
|
||||
}
|
||||
if (T_old && T_old != refmap->min_table) {
|
||||
_flatcc_refmap_free(T_old);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
flatcc_refmap_ref_t flatcc_refmap_insert(flatcc_refmap_t *refmap, const void *src, flatcc_refmap_ref_t ref)
|
||||
{
|
||||
struct flatcc_refmap_item *T;
|
||||
size_t N, i, j, k;
|
||||
|
||||
if (src == 0) return ref;
|
||||
if (_flatcc_refmap_above_load_factor(refmap->count, refmap->buckets)) {
|
||||
if (flatcc_refmap_resize(refmap, refmap->count * 2)) {
|
||||
return flatcc_refmap_not_found; /* alloc failed */
|
||||
}
|
||||
}
|
||||
T = refmap->table;
|
||||
N = refmap->buckets - 1;
|
||||
k = _flatcc_refmap_hash(src);
|
||||
i = 0;
|
||||
j = _flatcc_refmap_probe(k, i, N);
|
||||
while (T[j].src) {
|
||||
if (T[j].src == src) {
|
||||
return T[j].ref = ref;
|
||||
}
|
||||
++i;
|
||||
j = _flatcc_refmap_probe(k, i, N);
|
||||
}
|
||||
++refmap->count;
|
||||
T[j].src = src;
|
||||
return T[j].ref = ref;
|
||||
}
|
||||
|
||||
flatcc_refmap_ref_t flatcc_refmap_find(flatcc_refmap_t *refmap, const void *src)
|
||||
{
|
||||
struct flatcc_refmap_item *T;
|
||||
size_t N, i, j, k;
|
||||
|
||||
if (refmap->count == 0) {
|
||||
return flatcc_refmap_not_found;
|
||||
}
|
||||
T = refmap->table;
|
||||
N = refmap->buckets - 1;
|
||||
k = _flatcc_refmap_hash(src);
|
||||
i = 0;
|
||||
j = _flatcc_refmap_probe(k, i, N);
|
||||
while (T[j].src) {
|
||||
if (T[j].src == src) return T[j].ref;
|
||||
++i;
|
||||
j = _flatcc_refmap_probe(k, i, N);
|
||||
}
|
||||
return flatcc_refmap_not_found;
|
||||
}
|
||||
|
||||
/*
|
||||
* To run test from project root:
|
||||
*
|
||||
* cc -D FLATCC_REFMAP_TEST -I include src/runtime/refmap.c -o test_refmap && ./test_refmap
|
||||
*
|
||||
*/
|
||||
#ifdef FLATCC_REFMAP_TEST
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef FLATCC_REFMAP_H
|
||||
#include "flatcc/flatcc_refmap.h"
|
||||
#endif
|
||||
|
||||
#define test(x) do { if (!(x)) { fprintf(stderr, "%02d: refmap test failed\n", __LINE__); exit(-1); } } while (0)
|
||||
#define test_start() fprintf(stderr, "starting refmap test ...\n")
|
||||
#define test_ok() fprintf(stderr, "refmap test succeeded\n")
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
int data[1000];
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int c = 3;
|
||||
flatcc_refmap_t refmap;
|
||||
|
||||
flatcc_refmap_init(&refmap);
|
||||
|
||||
test(flatcc_refmap_find(&refmap, &a) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_find(&refmap, &b) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_find(&refmap, &c) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_find(&refmap, 0) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_find(&refmap, &a) == 0);
|
||||
|
||||
test(flatcc_refmap_insert(&refmap, &a, 42) == 42);
|
||||
test(flatcc_refmap_find(&refmap, &a) == 42);
|
||||
test(flatcc_refmap_find(&refmap, &b) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_find(&refmap, &c) == flatcc_refmap_not_found);
|
||||
test(flatcc_refmap_insert(&refmap, &a, 42) == 42);
|
||||
test(flatcc_refmap_find(&refmap, &a) == 42);
|
||||
test(refmap.count == 1);
|
||||
test(flatcc_refmap_insert(&refmap, &a, 43) == 43);
|
||||
test(flatcc_refmap_find(&refmap, &a) == 43);
|
||||
test(refmap.count == 1);
|
||||
test(flatcc_refmap_insert(&refmap, &b, -10) == -10);
|
||||
test(flatcc_refmap_insert(&refmap, &c, 100) == 100);
|
||||
test(refmap.count == 3);
|
||||
test(flatcc_refmap_find(&refmap, &a) == 43);
|
||||
test(flatcc_refmap_find(&refmap, &b) == -10);
|
||||
test(flatcc_refmap_find(&refmap, &c) == 100);
|
||||
|
||||
test(flatcc_refmap_insert(&refmap, 0, 1000) == 1000);
|
||||
test(flatcc_refmap_find(&refmap, 0) == 0);
|
||||
test(refmap.count == 3);
|
||||
|
||||
test(flatcc_refmap_insert(&refmap, &b, 0) == 0);
|
||||
test(flatcc_refmap_find(&refmap, &b) == 0);
|
||||
test(refmap.count == 3);
|
||||
|
||||
flatcc_refmap_reset(&refmap);
|
||||
test(refmap.count == 0);
|
||||
test(refmap.buckets > 0);
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
test(flatcc_refmap_insert(&refmap, data + i, i + 42) == i + 42);
|
||||
}
|
||||
test(refmap.count == 1000);
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
test(flatcc_refmap_find(&refmap, data + i) == i + 42);
|
||||
}
|
||||
flatcc_refmap_clear(&refmap);
|
||||
test(refmap.count == 0);
|
||||
test(refmap.buckets == 0);
|
||||
test_ok();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* FLATCC_REFMAP_TEST */
|
||||
Reference in New Issue
Block a user