Browse Source

hashmap can now take non scalar types as keys

master
Felix Brendel 5 years ago
parent
commit
3a8b477563
4 changed files with 144 additions and 63 deletions
  1. +2
    -2
      error.hpp
  2. +33
    -21
      hashmap.hpp
  3. +8
    -4
      print.hpp
  4. +101
    -36
      test.cpp

+ 2
- 2
error.hpp View File

@@ -53,11 +53,11 @@ auto create_error(const char* c_func_name, const char* c_file_name,
#define create_generic_error(...) \
__create_error("generic", __VA_ARGS__)

#define assert(condition, format, ...) \
#define assert(condition, ...) \
do { \
if (!(condition)) { \
char* msg; \
print_to_string(&msg, format, __VA_ARGS__); \
print_to_string(&msg, __VA_ARGS__); \
create_assertion_error("Assertion-error: %s\n" \
" condition: %s\n" \
" in: %s:%d", \


+ 33
- 21
hashmap.hpp View File

@@ -44,7 +44,11 @@ struct Hash_Map {
struct HM_Cell {
key_type original;
u64 hash;
bool deleted;
enum struct Occupancy : u8 {
Avaliable = 0,
Occupied,
Deleted
} occupancy;
value_type object;
}* data;

@@ -55,20 +59,20 @@ struct Hash_Map {
}

void dealloc() {
free(data);
data = nullptr;
}
free(data);
data = nullptr;
}

s32 get_index_of_living_cell_if_it_exists(key_type key, u64 hash_val) {
// s32 index = hash_val & (current_capacity - 1);
s32 index = hash_val % current_capacity;
HM_Cell cell = data[index];
/* test if cell exists at that index */
if (cell.original) {
/* check if strings match */
/* test if there is or was something there */
if (cell.occupancy != HM_Cell::Occupancy::Avaliable) {
/* check if objects match */
if (hm_objects_match(key, cell.original)) {
/* we found it, now check it it is deleted: */
if (cell.deleted) {
if (cell.occupancy == HM_Cell::Occupancy::Deleted) {
/* we found it but it was deleted, we */
/* dont have to check for collisions then */
return -1;
@@ -77,16 +81,21 @@ struct Hash_Map {
return index;
}
} else {
/* strings dont match, this means we have */
/* a collision. We just search forward */
/* objects dont match, this means we have */
/* a collision. We just search forward */
for (u32 i = 0; i < current_capacity; ++i) {
u32 new_idx = (i + index) % current_capacity;
cell = data[new_idx];
if (!cell.original)
/* If we find a avaliable cell while looking */
/* forward, the object is not in the hm */
if (cell.occupancy == HM_Cell::Occupancy::Avaliable)
return -1;
/* If the objects don't match, keep looking */
if (!hm_objects_match(key, cell.original))
continue;
if (cell.deleted)
/* TODO(Felix): If the objects do match, */
/* and it is deleted, we should return -1? */
if (cell.occupancy == HM_Cell::Occupancy::Deleted)
continue;
return new_idx;
}
@@ -108,8 +117,11 @@ struct Hash_Map {

key_type search_key_to_object(value_type v) {
for (u32 i = 0; i < current_capacity; ++i) {
if (data[i].object == v && !data[i].deleted)
if (data[i].object == v &&
data[i].occupancy == HM_Cell::Occupancy::Occupied)
{
return data[i].original;
}
}
return nullptr;
}
@@ -120,7 +132,7 @@ struct Hash_Map {
// QUESTION(Felix): Does it make sense to
// ret.reserve(this->cell_count)?
for (u32 i = 0; i < current_capacity; ++i) {
if (data[i].original && !data[i].deleted)
if (data[i].occupancy == HM_Cell::Occupancy::Occupied)
ret.append(data[i].original);
}
return ret;
@@ -142,15 +154,15 @@ struct Hash_Map {
void delete_object(key_type key) {
s32 index = get_index_of_living_cell_if_it_exists(key, hm_hash((key_type)key));
if (index != -1) {
data[index].deleted = true;
data[index].occupancy = HM_Cell::Occupancy::Deleted;
}
}

void set_object(key_type key, value_type obj, u64 hash_val) {
u32 index = hash_val % current_capacity;

/* if we the desired cell is just empty, write to it and done :) */
if (!data[index].original) {
/* if we the desired cell is avaliable, write to it and done :) */
if (data[index].occupancy == HM_Cell::Occupancy::Avaliable) {
/* insert new cell into desired slot */
++cell_count;
} else {
@@ -168,7 +180,7 @@ struct Hash_Map {
/* insert all old items again */
for (u32 i = 0; i < current_capacity/4; ++i) {
auto cell = old_data[i];
if (cell.original) {
if (cell.occupancy == HM_Cell::Occupancy::Occupied) {
set_object(cell.original, cell.object, cell.hash);
}
}
@@ -180,7 +192,7 @@ struct Hash_Map {
/* preventing gotos using lambdas! */
[&]{
for (u32 i = index; i < current_capacity; ++i) {
if (!data[i].original ||
if (data[i].occupancy == HM_Cell::Occupancy::Avaliable ||
hm_objects_match(data[i].original, key))
{
index = i;
@@ -188,7 +200,7 @@ struct Hash_Map {
}
}
for (u32 i = 0; i < index; ++i) {
if (!data[i].original ||
if (data[i].occupancy == HM_Cell::Occupancy::Avaliable ||
hm_objects_match(data[i].original, key))
{
index = i;
@@ -199,7 +211,7 @@ struct Hash_Map {
}
}

data[index].deleted = false;
data[index].occupancy = HM_Cell::Occupancy::Occupied;
data[index].original = key;
data[index].hash = hash_val;
data[index].object = obj;


+ 8
- 4
print.hpp View File

@@ -269,10 +269,14 @@ int maybe_fprintf(FILE* file, static_string format, int* pos, va_list* arg_list)

// printf("\ntest:: len(%s) = %d\n", temp, writen_len+1);

writen_len = vfprintf(file, temp, *arg_list);

// NOTE(Felix): For WSL Linux we have to manually overstep the
// used args
/// NOTE(Felix): Somehow we have to pass a copy of the list to vfprintf
// because otherwise it destroys it on some platforms :(
va_list arg_list_copy;
va_copy(arg_list_copy, *arg_list);
writen_len = vfprintf(file, temp, arg_list_copy);
va_end(arg_list_copy);

// NOTE(Felix): maually overstep the args that vfprintf will have used
for (int i = 0; i < used_arg_values; ++i) {
va_arg(*arg_list, void*);
}


+ 101
- 36
test.cpp View File

@@ -1,62 +1,127 @@
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

#include "./types.hpp"
#include "./hooks.hpp"

u32 hm_hash(u32 u);
inline bool hm_objects_match(u32 a, u32 b);
struct Key;
u32 hm_hash(Key u);
inline bool hm_objects_match(Key a, Key b);

#include "./error.hpp"
#include "./hooks.hpp"
#include "./hashmap.hpp"

#include "./error.hpp"
#include "./print.hpp"


int print_dots(FILE* f) {
u32 hm_hash(u32 u) {
return ((u64)u * 2654435761) % 4294967296;
}

inline bool hm_objects_match(u32 a, u32 b) {
return a == b;
}

struct Key {
int x;
int y;
int z;
};


u32 hm_hash(Key u) {
return ((u.y ^ (u.x << 1)) >> 1) ^ u.z;
}

inline bool hm_objects_match(Key a, Key b) {
return a.x == b.x
&& a.y == b.y
&& a.z == b.z;
}


auto print_dots(FILE* f) -> u32 {
return print_to_file(f, "...");
}

void test_printer() {
// u32 arr[] = {1,2,3,4,1,1,3};
// f32 f_arr[] = {1.1,2.1,3.2};
auto test_printer() -> void {
u32 arr[] = {1,2,3,4,1,1,3};
f32 f_arr[] = {1.1,2.1,3.2};


// init_printer();
// register_printer("dots", print_dots, Printer_Function_Type::_void);
register_printer("dots", print_dots, Printer_Function_Type::_void);

// u32 u1 = -1;
// u64 u2 = -1;
u32 u1 = -1;
u64 u2 = -1;

// char* str;
// print_to_string(&str, " - %{dots[5]} %{->} <> %{->,2}\n", &u1, &arr, nullptr);
// print("---> %{->char}", str);
char* str;
print_to_string(&str, " - %{dots[5]} %{->} <> %{->,2}\n", &u1, &arr, nullptr);
print("---> %{->char}", str);

// print(" - %{dots[3]}\n");
// print(" - %{u32} %{u64}\n", u1, u2);
// print(" - %{u32} %{u32} %{u32}\n", 2, 5, 7);
// print(" - %{f32} %{f32} %{f32}\n", 2.0, 5.0, 7.0);
// print(" - %{u32} %{bool} %{->char}\n", 2, true, "hello");
// print(" - %{f32[3]}\n", f_arr);
// print(" - %{f32,3}\n", 44.9, 55.1, 66.2);
// print(" - %{u32[5]}\n", arr);
// print(" - %{u32[*]}\n", arr, 4);
// print(" - %{u32,5}\n", 1,2,3,4,1,2);
// print(" - %{unknown%d}\n", 1);
// print(" - %{s32,3}\n", -1,200,-300);
// print(" - %{->} <> %{->,2}\n", &u1, &arr, nullptr);
print(" - %{dots[3]}\n");
print(" - %{u32} %{u64}\n", u1, u2);
print(" - %{u32} %{u32} %{u32}\n", 2, 5, 7);
print(" - %{f32} %{f32} %{f32}\n", 2.0, 5.0, 7.0);
print(" - %{u32} %{bool} %{->char}\n", 2, true, "hello");
print(" - %{f32[3]}\n", f_arr);
print(" - %{f32,3}\n", 44.9, 55.1, 66.2);
print(" - %{u32[5]}\n", arr);
print(" - %{u32[*]}\n", arr, 4);
print(" - %{u32,5}\n", 1,2,3,4,1,2);
print(" - %{unknown%d}\n", 1);
print(" - %{s32,3}\n", -1,200,-300);
print(" - %{->} <> %{->,2}\n", &u1, &arr, nullptr);

}

s32 main(s32 argc, char* argv[]) {
auto test_hm() -> void {
Hash_Map<u32, u32> h1;
h1.alloc();
defer { h1.dealloc(); };

h1.set_object(1, 2);
h1.set_object(2, 4);
h1.set_object(3, 6);
h1.set_object(4, 8);

assert(h1.key_exists(1), "key shoud exist");
assert(h1.key_exists(2), "key shoud exist");
assert(h1.key_exists(3), "key shoud exist");
assert(h1.key_exists(4), "key shoud exist");
assert(!h1.key_exists(5), "key shoud not exist");

assert(h1.get_object(1) == 2, "value should be correct");
assert(h1.get_object(2) == 4, "value should be correct");
assert(h1.get_object(3) == 6, "value should be correct");
assert(h1.get_object(4) == 8, "value should be correct");


Hash_Map<Key, u32> h2;
h2.alloc();
defer { h2.dealloc(); };

h2.set_object({.x = 1, .y = 2, .z = 3}, 1);
h2.set_object({.x = 3, .y = 3, .z = 3}, 3);

assert(h2.key_exists({.x = 1, .y = 2, .z = 3}), "key shoud exist");
assert(h2.key_exists({.x = 3, .y = 3, .z = 3}), "key shoud exist");

// test_printer();
assert(h2.get_object({.x = 1, .y = 2, .z = 3}) == 1, "value should be correct");
assert(h2.get_object({.x = 3, .y = 3, .z = 3}) == 3, "value should be correct");



}

s32 main(s32 argc, char* argv[]) {
init_printer();
create_generic_error("nothing to lex was found:\n"
" in %{color<}%{->char}%{>color}\n"
" at %{color<}%{->char}%{>color}\n"
"bottom text\n",
console_green,
"some file name",
console_cyan,
"yesssssss");

test_hm();



return 0;
}

Loading…
Cancel
Save