good progress on maps
This commit is contained in:
@@ -138,7 +138,7 @@ allocator_t allocator_from_arena(arena_allocator_t *this);
|
|||||||
// dynamic arrays //////////////////////////////////////////////////////////////
|
// dynamic arrays //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
typedef struct dyn_array_header {
|
typedef struct dyn_array_header {
|
||||||
size_t n_items, capacity, itemsize;
|
size_t n_items, capacity, item_size;
|
||||||
allocator_t allocator;
|
allocator_t allocator;
|
||||||
union {
|
union {
|
||||||
// allocates a little more memory than needed, but who cares?
|
// allocates a little more memory than needed, but who cares?
|
||||||
@@ -149,7 +149,7 @@ typedef struct dyn_array_header {
|
|||||||
|
|
||||||
typedef struct dyn_array_create_func_args {
|
typedef struct dyn_array_create_func_args {
|
||||||
allocator_t allocator;
|
allocator_t allocator;
|
||||||
size_t itemsize;
|
size_t item_size;
|
||||||
size_t initial_capacity;
|
size_t initial_capacity;
|
||||||
const char *file;
|
const char *file;
|
||||||
int line;
|
int line;
|
||||||
@@ -157,7 +157,7 @@ typedef struct dyn_array_create_func_args {
|
|||||||
void *dyn_array_create_func(dyn_array_create_func_args_t args);
|
void *dyn_array_create_func(dyn_array_create_func_args_t args);
|
||||||
|
|
||||||
#define make_arr(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \
|
#define make_arr(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \
|
||||||
.itemsize = sizeof(TYPE), \
|
.item_size = sizeof(TYPE), \
|
||||||
.file = __FILE__, \
|
.file = __FILE__, \
|
||||||
.line = __LINE__, \
|
.line = __LINE__, \
|
||||||
.allocator = __VA_ARGS__, \
|
.allocator = __VA_ARGS__, \
|
||||||
@@ -165,7 +165,7 @@ void *dyn_array_create_func(dyn_array_create_func_args_t args);
|
|||||||
|
|
||||||
#define init_arr(PTR, ...) do { \
|
#define init_arr(PTR, ...) do { \
|
||||||
PTR = dyn_array_create_func((dyn_array_create_func_args_t){ \
|
PTR = dyn_array_create_func((dyn_array_create_func_args_t){ \
|
||||||
.itemsize = sizeof(*PTR), \
|
.item_size = sizeof(*PTR), \
|
||||||
.file = __FILE__, \
|
.file = __FILE__, \
|
||||||
.line = __LINE__, \
|
.line = __LINE__, \
|
||||||
.allocator = __VA_ARGS__, \
|
.allocator = __VA_ARGS__, \
|
||||||
@@ -239,11 +239,12 @@ typedef unsigned int (*hash_func_t)(void *key);
|
|||||||
typedef bool (*equals_func_t)(void *key1, void *key2);
|
typedef bool (*equals_func_t)(void *key1, void *key2);
|
||||||
|
|
||||||
typedef struct map_header {
|
typedef struct map_header {
|
||||||
int capacity;
|
unsigned int capacity;
|
||||||
int itemsize;
|
unsigned int item_size;
|
||||||
int keysize;
|
unsigned int key_size;
|
||||||
int mapping_capacity;
|
unsigned int mapping_capacity;
|
||||||
int n_items;
|
unsigned int n_items;
|
||||||
|
unsigned int key_offset;
|
||||||
allocator_t allocator;
|
allocator_t allocator;
|
||||||
hash_func_t hash;
|
hash_func_t hash;
|
||||||
equals_func_t equals;
|
equals_func_t equals;
|
||||||
@@ -259,9 +260,10 @@ typedef struct map_create_func_args {
|
|||||||
const char *file;
|
const char *file;
|
||||||
hash_func_t hash; // OPTIONAL
|
hash_func_t hash; // OPTIONAL
|
||||||
equals_func_t equals; // OPTIONAL
|
equals_func_t equals; // OPTIONAL
|
||||||
int initial_capacity; // OPTIONAL
|
unsigned int initial_capacity; // OPTIONAL
|
||||||
int itemsize;
|
unsigned int item_size;
|
||||||
int keysize;
|
unsigned int key_offset;
|
||||||
|
unsigned int key_size;
|
||||||
int line;
|
int line;
|
||||||
} map_create_func_args_t;
|
} map_create_func_args_t;
|
||||||
|
|
||||||
@@ -271,16 +273,24 @@ void *map_create_func(map_create_func_args_t args);
|
|||||||
#define init_map(PTR, ...) do { \
|
#define init_map(PTR, ...) do { \
|
||||||
STATIC_ASSERT(((size_t)PTR) == ((size_t)(&PTR->key))); \
|
STATIC_ASSERT(((size_t)PTR) == ((size_t)(&PTR->key))); \
|
||||||
PTR = map_create_func((map_create_func_args_t) { \
|
PTR = map_create_func((map_create_func_args_t) { \
|
||||||
.itemsize=sizeof(*PTR), \
|
.item_size=sizeof(*PTR), \
|
||||||
.keysize=sizeof(PTR->key), \
|
.key_size=sizeof(PTR->key), \
|
||||||
.file=__FILE__, \
|
.file=__FILE__, \
|
||||||
.line=__LINE__, \
|
.line=__LINE__, \
|
||||||
.allocator=__VA_ARGS__ \
|
.allocator=__VA_ARGS__ \
|
||||||
|
.key_offset=offsetof(PTR->key), \
|
||||||
}); \
|
}); \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
int map_len(void *this);
|
int map_len(void *this);
|
||||||
int map_cap(void *this);
|
int map_cap(void *this);
|
||||||
|
uint32_t fnv_1a(const uint8_t *bytes, const unsigned int size);
|
||||||
|
/*
|
||||||
|
* Hashes modulo the header->mapping capacity. Will keep hashing the hash until
|
||||||
|
* a free or gravestone slot is found. Does not write to the mapping array, only
|
||||||
|
* returns the next valid index to overwrite. Uses default or provided hashing and equals functions
|
||||||
|
*/
|
||||||
|
unsigned int map_pair_hash(void *this, void *pair);
|
||||||
|
|
||||||
// CLI /////////////////////////////////////////////////////////////////////////
|
// CLI /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -5,13 +5,13 @@
|
|||||||
void *dyn_array_create_func(dyn_array_create_func_args_t args) {
|
void *dyn_array_create_func(dyn_array_create_func_args_t args) {
|
||||||
dyn_array_header_t *header = allocator_alloc_func(
|
dyn_array_header_t *header = allocator_alloc_func(
|
||||||
args.allocator,
|
args.allocator,
|
||||||
sizeof(dyn_array_header_t) + args.itemsize * args.initial_capacity,
|
sizeof(dyn_array_header_t) + args.item_size * args.initial_capacity,
|
||||||
args.file,
|
args.file,
|
||||||
args.line
|
args.line
|
||||||
);
|
);
|
||||||
header->n_items = 0;
|
header->n_items = 0;
|
||||||
header->capacity = args.initial_capacity;
|
header->capacity = args.initial_capacity;
|
||||||
header->itemsize = args.itemsize;
|
header->item_size = args.item_size;
|
||||||
header->allocator = args.allocator;
|
header->allocator = args.allocator;
|
||||||
return &header->bytes;
|
return &header->bytes;
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int
|
|||||||
header = allocator_resize_func(
|
header = allocator_resize_func(
|
||||||
header->allocator,
|
header->allocator,
|
||||||
header,
|
header,
|
||||||
sizeof(dyn_array_header_t) + header->itemsize * new_capacity,
|
sizeof(dyn_array_header_t) + header->item_size * new_capacity,
|
||||||
file,
|
file,
|
||||||
line
|
line
|
||||||
);
|
);
|
||||||
@@ -71,8 +71,8 @@ bool dyn_array_contains_func(void *this, uint8_t *value) {
|
|||||||
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
for (size_t i = 0; i < header->n_items; i++) {
|
for (size_t i = 0; i < header->n_items; i++) {
|
||||||
bool is_equal = true;
|
bool is_equal = true;
|
||||||
for (size_t off = 0; off < header->itemsize; off++) {
|
for (size_t off = 0; off < header->item_size; off++) {
|
||||||
if (header->bytes[i*header->itemsize+off] != value[off]) {
|
if (header->bytes[i*header->item_size+off] != value[off]) {
|
||||||
is_equal = false;
|
is_equal = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -86,10 +86,10 @@ bool dyn_array_contains_func(void *this, uint8_t *value) {
|
|||||||
|
|
||||||
bool dyn_array_contains_eq_func(void *this, uint8_t *value, dyn_array_eq_fn eq) {
|
bool dyn_array_contains_eq_func(void *this, uint8_t *value, dyn_array_eq_fn eq) {
|
||||||
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
size_t itemsize = header->itemsize;
|
size_t item_size = header->item_size;
|
||||||
|
|
||||||
for (size_t i = 0; i < header->n_items; i++) {
|
for (size_t i = 0; i < header->n_items; i++) {
|
||||||
void *element = &header->bytes[i * itemsize];
|
void *element = &header->bytes[i * item_size];
|
||||||
if (eq(element, value)) {
|
if (eq(element, value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -116,5 +116,5 @@ void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int
|
|||||||
|
|
||||||
void arr_qsort(void *this, dyn_array_cmp_fn cmp_fn) {
|
void arr_qsort(void *this, dyn_array_cmp_fn cmp_fn) {
|
||||||
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
qsort(this, header->n_items, header->itemsize, cmp_fn);
|
qsort(this, header->n_items, header->item_size, cmp_fn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
#include "cig.h"
|
#include "cig.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define FREE (-1)
|
#define FLAG_FREE (-1)
|
||||||
#define GRAVESTONE (-2)
|
#define FLAG_GRAVESTONE (-2)
|
||||||
|
|
||||||
static inline int mapping_cap(int capacity) {
|
static inline unsigned int mapping_cap(unsigned int capacity) {
|
||||||
return capacity * 2;
|
return capacity * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct internal_map_sizes {
|
typedef struct internal_map_sizes {
|
||||||
int bytes;
|
int bytes;
|
||||||
int cap_of_items_arr;
|
int real_cap_of_items_arr;
|
||||||
} internal_map_sizes_t;
|
} internal_map_sizes_t;
|
||||||
|
|
||||||
internal_map_sizes_t internal_map_sizes(int capacity, int itemsize) {
|
internal_map_sizes_t internal_map_sizes(unsigned int capacity, unsigned int item_size) {
|
||||||
const size_t cap_of_index_arr =
|
const size_t cap_of_index_arr =
|
||||||
sizeof(int) * mapping_cap(capacity);
|
sizeof(int) * mapping_cap(capacity);
|
||||||
const size_t INC = ALIGN_OF(int);
|
const size_t INC = ALIGN_OF(int);
|
||||||
const size_t SIZE = itemsize * capacity;
|
const size_t SIZE = item_size * capacity;
|
||||||
const size_t cap_of_items_arr = (SIZE + INC - 1) / INC;
|
const size_t cap_of_items_arr = (SIZE + INC - 1) / INC;
|
||||||
const size_t bytes =
|
const size_t bytes =
|
||||||
sizeof(map_header_t) + cap_of_items_arr + cap_of_index_arr;
|
sizeof(map_header_t) + cap_of_items_arr + cap_of_index_arr;
|
||||||
return (internal_map_sizes_t){
|
return (internal_map_sizes_t){
|
||||||
.bytes=bytes,
|
.bytes=bytes,
|
||||||
.cap_of_items_arr=cap_of_items_arr,
|
.real_cap_of_items_arr=cap_of_items_arr,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ void *map_create_func(
|
|||||||
) {
|
) {
|
||||||
internal_map_sizes_t sizes = internal_map_sizes(
|
internal_map_sizes_t sizes = internal_map_sizes(
|
||||||
args.initial_capacity,
|
args.initial_capacity,
|
||||||
args.itemsize
|
args.item_size
|
||||||
);
|
);
|
||||||
map_header_t *header =
|
map_header_t *header =
|
||||||
allocator_alloc_func(
|
allocator_alloc_func(
|
||||||
@@ -44,16 +44,16 @@ void *map_create_func(
|
|||||||
|
|
||||||
header->n_items = 0;
|
header->n_items = 0;
|
||||||
header->capacity = args.initial_capacity;
|
header->capacity = args.initial_capacity;
|
||||||
header->itemsize = args.itemsize;
|
header->item_size = args.item_size;
|
||||||
header->keysize = args.keysize;
|
header->key_size = args.key_size;
|
||||||
header->allocator = args.allocator;
|
header->allocator = args.allocator;
|
||||||
header->hash = args.hash;
|
header->hash = args.hash;
|
||||||
header->equals = args.equals;
|
header->equals = args.equals;
|
||||||
header->mapping_arr = (int*) &header->bytes[sizes.cap_of_items_arr];
|
header->mapping_arr = (int*) &header->bytes[sizes.real_cap_of_items_arr];
|
||||||
header->mapping_capacity = mapping_cap(args.initial_capacity);
|
header->mapping_capacity = mapping_cap(args.initial_capacity);
|
||||||
|
|
||||||
for ( int i = 0; i < header->mapping_capacity; i++ ) {
|
for ( int i = 0; i < header->mapping_capacity; i++ ) {
|
||||||
header->mapping_arr[i] = -1;
|
header->mapping_arr[i] = FLAG_FREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return header->bytes;
|
return header->bytes;
|
||||||
@@ -61,30 +61,42 @@ void *map_create_func(
|
|||||||
|
|
||||||
void *map_grow_func(void *this, const char *file, int line) {
|
void *map_grow_func(void *this, const char *file, int line) {
|
||||||
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
|
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
|
||||||
int new_size = header->n_items + 1;
|
int new_n_items = header->n_items + 1;
|
||||||
|
|
||||||
if (header->capacity < new_size) {
|
if (header->capacity < new_n_items) {
|
||||||
allocator_t allocator = header->allocator;
|
allocator_t allocator = header->allocator;
|
||||||
int new_capacity = 1;
|
unsigned int new_capacity = 1;
|
||||||
if (header->capacity > 0) {
|
if (header->capacity > 0) {
|
||||||
new_capacity = header->capacity * 2;
|
new_capacity = header->capacity * 2;
|
||||||
}
|
}
|
||||||
internal_map_sizes_t new_size = internal_map_sizes(
|
internal_map_sizes_t new_size = internal_map_sizes(
|
||||||
new_capacity,
|
new_capacity,
|
||||||
header->itemsize
|
header->item_size
|
||||||
);
|
);
|
||||||
header = allocator_resize_func(allocator, header, new_size.bytes, file, line);
|
header = allocator_resize_func(allocator, header, new_size.bytes, file, line);
|
||||||
|
header->mapping_arr = (int*) &header->bytes[new_size.real_cap_of_items_arr];
|
||||||
|
header->mapping_capacity = mapping_cap(new_capacity);
|
||||||
|
header->capacity=new_capacity;
|
||||||
|
|
||||||
static_assert(0, "Overwrite the mapping arr with -1, then iterate"
|
// first we have to clear the entire mapping array
|
||||||
"through the items arr and find the hashes of the values to populate the"
|
for ( unsigned int i = 0; i < header->mapping_capacity; i++ ) {
|
||||||
"mappings");
|
header->mapping_arr[i] = FLAG_FREE;
|
||||||
|
|
||||||
static_assert(0, "make sure the mapping_arr field is set to the new and"
|
|
||||||
"correct pointer!");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header->n_items = new_size;
|
// Then we have to re-hash and map all the items in the existing map.
|
||||||
|
// The reason for re-hashing is that all hashes are moduloed by the
|
||||||
|
// size of the mapping array, so the resulting hash will likely change.
|
||||||
|
for ( unsigned int i = 0; i < header->n_items; i++ ) {
|
||||||
|
void *pair = &this[i*header->item_size];
|
||||||
|
// TODO: make the map_pair_hash more private? would be nice to just
|
||||||
|
// pass in the header, and I don't really think it is needed
|
||||||
|
// directly by the user of the library.
|
||||||
|
unsigned int mapping_index = map_pair_hash(this, pair);
|
||||||
|
header->mapping_arr[mapping_index] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header->n_items = new_n_items;
|
||||||
return header->bytes;
|
return header->bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,12 +115,12 @@ void map_shrink_func(void *this, const char *file, int line) {
|
|||||||
static_assert(0, "Hash the key at the removed item to find it in the mapping array, and set it to the gravestone value.");
|
static_assert(0, "Hash the key at the removed item to find it in the mapping array, and set it to the gravestone value.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t fnv_1a(const uint8_t *bytes, const int length) {
|
uint32_t fnv_1a(const uint8_t *bytes, const unsigned int size) {
|
||||||
// Yanked from: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
// Yanked from: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
const uint32_t PRIME = 0x01000193;
|
const uint32_t PRIME = 0x01000193;
|
||||||
const uint32_t OFFSET = 0x811c9dc5;
|
const uint32_t OFFSET = 0x811c9dc5;
|
||||||
uint32_t hash = OFFSET;
|
uint32_t hash = OFFSET;
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
uint32_t byte = (uint32_t)bytes[i];
|
uint32_t byte = (uint32_t)bytes[i];
|
||||||
hash = hash ^ byte;
|
hash = hash ^ byte;
|
||||||
hash = hash * PRIME;
|
hash = hash * PRIME;
|
||||||
@@ -116,12 +128,36 @@ static inline uint32_t fnv_1a(const uint8_t *bytes, const int length) {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int map_hash(void *this, void *value) {
|
static void *internal_cig_key_ptr_from_pair_ptr(void *this, void *pair) {
|
||||||
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
|
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
|
||||||
if (header->hash) {
|
return &pair[header->key_offset];
|
||||||
return header->hash(value);
|
|
||||||
}
|
}
|
||||||
return (unsigned int) fnv_1a(value, header->keysize) % header->mapping_capacity;
|
|
||||||
|
// TODO: just make these internal probably
|
||||||
|
bool map_pair_key_equals(void *this, void *pair1, void *pair2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int map_pair_hash(void *this, void *pair) {
|
||||||
|
map_header_t *header = PTR_FROM_FIELD_PTR(map_header_t, bytes, this);
|
||||||
|
void *key = internal_cig_key_ptr_from_pair_ptr(this, pair);
|
||||||
|
|
||||||
|
// find the initial index based on hash
|
||||||
|
uint32_t index;
|
||||||
|
if (header->hash != NULL) {
|
||||||
|
index = header->hash(key) % header->mapping_capacity;
|
||||||
|
} else {
|
||||||
|
index = fnv_1a(key, header->key_size) % header->mapping_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
int existing_index = header->mapping_arr[index];
|
||||||
|
while (!(existing_index == FLAG_FREE || existing_index == FLAG_GRAVESTONE)) {
|
||||||
|
index = fnv_1a(
|
||||||
|
(const uint8_t *) &index,
|
||||||
|
sizeof(index)
|
||||||
|
) % header->mapping_capacity;
|
||||||
|
int existing_index = header->mapping_arr[index];
|
||||||
|
}
|
||||||
|
return (unsigned int) index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int map_len(void *this) {
|
int map_len(void *this) {
|
||||||
@@ -140,6 +176,6 @@ int map_cap(void *this) {
|
|||||||
return header->capacity;
|
return header->capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef FREE
|
#undef FLAG_FREE
|
||||||
#undef GRAVESTONE
|
#undef FLAG_GRAVESTONE
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ Test(arena_allocator, resize_with_zero_capacity) {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
size_t itemsize;
|
size_t item_size;
|
||||||
allocator_t allocator;
|
allocator_t allocator;
|
||||||
uint8_t bytes[];
|
uint8_t bytes[];
|
||||||
} test_header_t;
|
} test_header_t;
|
||||||
@@ -148,7 +148,7 @@ Test(arena_allocator, resize_with_zero_capacity) {
|
|||||||
test_header_t *header = allocator_alloc(arena, sizeof(test_header_t));
|
test_header_t *header = allocator_alloc(arena, sizeof(test_header_t));
|
||||||
header->size = 0;
|
header->size = 0;
|
||||||
header->capacity = 0;
|
header->capacity = 0;
|
||||||
header->itemsize = sizeof(int);
|
header->item_size = sizeof(int);
|
||||||
|
|
||||||
// Resize to add space for 1 element
|
// Resize to add space for 1 element
|
||||||
header = allocator_resize(arena, header, sizeof(test_header_t) + sizeof(int) * 1);
|
header = allocator_resize(arena, header, sizeof(test_header_t) + sizeof(int) * 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user