hashmap
This commit is contained in:
parent
515c1200a7
commit
71bf582ba6
7 changed files with 159 additions and 61 deletions
|
@ -27,7 +27,8 @@ of making the region of memory allocated to more explicit.
|
|||
operations are included by default.
|
||||
- [x] Generic data-structures. The premier one is the
|
||||
[Dynamic Array](https://dylanfalconer.com/articles/dynamic-arrays-in-c), which
|
||||
brings the power of C++'s `std::vector` to C.
|
||||
brings the power of C++'s `std::vector` to C. There is also a HashMap, which is
|
||||
implemented as a [4-ary hash-trie](https://nullprogram.com/blog/2023/09/30/).
|
||||
- [x] A I/O system that supports both standard input/output and file
|
||||
input/output. It's built on a basic buffer type that you can flush to output
|
||||
data. This buffer type also acts as a **string builder**, by letting you append
|
||||
|
@ -38,6 +39,7 @@ primitives.
|
|||
- [ ] A 3D Math API with supports for Vectors and Matricies using SIMD.
|
||||
- [ ] A simple GUI initialization system that gets you a window with immediate-mode
|
||||
input and a Direct3D11 (Win32) / OpenGL (Linux) context to build a renderer with.
|
||||
- [ ] A simple immediate mode GUI with support for custom widgets.
|
||||
- [ ] An audio library. Windows implementation uses WASAPI, Linux uses ALSA.
|
||||
|
||||
---
|
||||
|
|
89
bastd/examples/data_structs.c
Normal file
89
bastd/examples/data_structs.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
#define BASTD_CLI
|
||||
#include "../../bastd.c"
|
||||
|
||||
/* The sl_TYPE macro defines the type of the generic. It is cleared after every
|
||||
include of "../slice.c"
|
||||
*/
|
||||
#define sl_TYPE I64
|
||||
#include "../slice.c"
|
||||
|
||||
|
||||
// SLICE types
|
||||
FUNCTION void
|
||||
fib_slice(Buffer *output, m_Allocator *perm)
|
||||
{
|
||||
// Slices can be created with a backing array to start with.
|
||||
I64 init[] = {0, 1};
|
||||
sl_I64 fib = sl_I64_create(init, 2);
|
||||
|
||||
for (;;) {
|
||||
// The data in a slice can be accessed like a normal array
|
||||
I64 a = fib.elems[fib.len - 2];
|
||||
I64 b = fib.elems[fib.len - 1];
|
||||
if (a + b > 255) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* The sl_TYPE_push function automatically doubles the capacity of the
|
||||
array if the slice is full and returns a pointer to a new element
|
||||
*/
|
||||
*sl_I64_push(&fib, perm) = a + b;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fib.len; i++) {
|
||||
Buffer_appendI64(output, fib.elems[i]);
|
||||
Buffer_appendU8(output, '\n');
|
||||
}
|
||||
Buffer_standardOutput(output);
|
||||
}
|
||||
|
||||
/* These generic structures may have more than one required macro */
|
||||
#define hm_KEY_TYPE S8
|
||||
#define hm_KEY_HASHPROC S8_hash_prime19
|
||||
#define hm_KEY_EQUALSPROC S8_equal
|
||||
#define hm_VAL_TYPE U64
|
||||
#include "../hashmap.c" // will define hm_S8_U64
|
||||
|
||||
// HASHMAPS
|
||||
FUNCTION void
|
||||
citypopulus_hashmap(Buffer *output, m_Allocator *perm)
|
||||
{
|
||||
// We always store a hashmap pointer, NOT the actual hashmap
|
||||
hm_S8_U64 *database = hm_EMPTY;
|
||||
|
||||
*hm_S8_U64_upsert(&database, S8("London"), perm) = 8800000;
|
||||
*hm_S8_U64_upsert(&database, S8("Lahore"), perm) = 13000000;
|
||||
*hm_S8_U64_upsert(&database, S8("Dhaka"), perm) = 21280000;
|
||||
*hm_S8_U64_upsert(&database, S8("Washington D.C."), perm) = 679000;
|
||||
*hm_S8_U64_upsert(&database, S8("Taxila"), perm) = 739000;
|
||||
|
||||
// Can't iterate through hashmap, but in exchange, accessing values is fast
|
||||
// NOTE: passing no allocator implies that you should NOT insert a value if it doesnt exist
|
||||
U64 dc_population = *hm_S8_U64_upsert(&database, S8("Washington D.C."), NIL);
|
||||
U64 lahore_population = *hm_S8_U64_upsert(&database, S8("Lahore"), NIL);
|
||||
U64 london_population = *hm_S8_U64_upsert(&database, S8("London"), NIL);
|
||||
|
||||
Buffer_appendI64(output, dc_population);
|
||||
Buffer_appendU8(output, '\n');
|
||||
Buffer_appendI64(output, lahore_population);
|
||||
Buffer_appendU8(output, '\n');
|
||||
Buffer_appendI64(output, london_population);
|
||||
Buffer_appendU8(output, '\n');
|
||||
|
||||
Buffer_standardOutput(output);
|
||||
}
|
||||
|
||||
CALLBACK_EXPORT os_ErrorCode
|
||||
os_entry(void)
|
||||
{
|
||||
m_Buddy buddy = m_Buddy_create(os_alloc(MEGA(2)), MEGA(2));
|
||||
m_Allocator perm = m_BUDDY_ALLOCATOR(buddy);
|
||||
|
||||
Buffer output = BUFFER(m_MAKE(U8, KILO(2), &perm), KILO(2));
|
||||
|
||||
fib_slice(&output, &perm);
|
||||
citypopulus_hashmap(&output, &perm);
|
||||
|
||||
|
||||
return os_ErrorCode_success;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
#define BASTD_CLI
|
||||
#include "../../bastd.c"
|
||||
|
||||
/* The sl_TYPE macro defines the type of the generic. It is cleared after every
|
||||
include of "../slice.c"
|
||||
*/
|
||||
#define sl_TYPE I64
|
||||
#include "../slice.c"
|
||||
|
||||
CALLBACK_EXPORT os_ErrorCode
|
||||
os_entry(void)
|
||||
{
|
||||
m_Buddy buddy = m_Buddy_create(os_alloc(MEGA(2)), MEGA(2));
|
||||
m_Allocator perm = m_BUDDY_ALLOCATOR(buddy);
|
||||
|
||||
// Slices can be created with a backing array to start with.
|
||||
I64 init[] = {0, 1};
|
||||
sl_I64 fib = sl_I64_create(init, 2);
|
||||
|
||||
for (;;) {
|
||||
// The data in a slice can be accessed like a normal array
|
||||
I64 a = fib.elems[fib.len - 2];
|
||||
I64 b = fib.elems[fib.len - 1];
|
||||
if (a + b > 255) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* The sl_TYPE_push function automatically doubles the capacity of the
|
||||
array if the slice is full and returns a pointer to a new element
|
||||
*/
|
||||
*sl_I64_push(&fib, &perm) = a + b;
|
||||
}
|
||||
|
||||
Buffer output = BUFFER(m_MAKE(U8, KILO(2), &perm), KILO(2));
|
||||
for (int i = 0; i < fib.len; i++) {
|
||||
Buffer_appendI64(&output, fib.elems[i]);
|
||||
Buffer_appendU8(&output, '\n');
|
||||
}
|
||||
Buffer_standardOutput(&output);
|
||||
|
||||
// Wait for you to press enter
|
||||
Buffer_appendS8(&output, S8("I've got a message for you. Press Enter to see it: "));
|
||||
Buffer_standardOutput(&output);
|
||||
Buffer_appendStandardInput(&output, 1024, &perm);
|
||||
|
||||
sl_S8 msgs = sl_S8_create(NIL, 0);
|
||||
*sl_S8_push(&msgs, &perm) = S8("C standard library!");
|
||||
*sl_S8_push(&msgs, &perm) = S8("a Bravely Arranged ");
|
||||
*sl_S8_push(&msgs, &perm) = S8("This is ");
|
||||
*sl_S8_push(&msgs, &perm) = S8("World! ");
|
||||
*sl_S8_push(&msgs, &perm) = S8("Hello, ");
|
||||
|
||||
for (; msgs.len > 0;) {
|
||||
Buffer_appendS8(&output, sl_S8_pop(&msgs));
|
||||
}
|
||||
Buffer_standardOutput(&output);
|
||||
|
||||
return os_ErrorCode_success;
|
||||
}
|
53
bastd/hashmap.c
Normal file
53
bastd/hashmap.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
// GENERIC FILE, REDEFINED EVERY INCLUDE
|
||||
#ifndef hm_KEY_TYPE
|
||||
#error "No hm_KEY_TYPE given"
|
||||
#endif
|
||||
|
||||
// hm_KEY_HASHPROC need signature "U64 procname(hm_KEY_TYPE)"
|
||||
#ifndef hm_KEY_HASHPROC
|
||||
#error "No hm_KEY_HASHPROC given"
|
||||
#endif
|
||||
|
||||
#ifndef hm_KEY_EQUALSPROC
|
||||
#error "No hm_KEY_EQUALSPROC given"
|
||||
#endif
|
||||
|
||||
#ifndef hm_VAL_TYPE
|
||||
#error "No hm_VAL_TYPE given"
|
||||
#endif
|
||||
|
||||
#define hm_NAME CONCAT(CONCAT(hm_, hm_KEY_TYPE), CONCAT(_, hm_VAL_TYPE))
|
||||
|
||||
typedef struct hm_NAME hm_NAME;
|
||||
struct hm_NAME {
|
||||
hm_NAME *child[4];
|
||||
hm_KEY_TYPE key;
|
||||
hm_VAL_TYPE val;
|
||||
};
|
||||
|
||||
// always store a pointer to the hashmap, not the actual value
|
||||
#define hm_EMPTY NIL
|
||||
|
||||
FUNCTION hm_VAL_TYPE *
|
||||
CONCAT(hm_NAME, _upsert) (hm_NAME **m, hm_KEY_TYPE key, m_Allocator *perm)
|
||||
{
|
||||
for (U64 h = hm_KEY_HASHPROC(key); *m; h <<= 2) {
|
||||
if (hm_KEY_EQUALSPROC(key, (*m)->key)) {
|
||||
return &(*m)->val;
|
||||
}
|
||||
m = &(*m)->child[h>>62];
|
||||
}
|
||||
if (perm == NIL) {
|
||||
// just searching, dont insert a new value
|
||||
return NIL;
|
||||
}
|
||||
*m = m_MAKE(hm_NAME, 1, perm);
|
||||
(*m)->key = key;
|
||||
return &(*m)->val;
|
||||
}
|
||||
|
||||
#undef hm_NAME
|
||||
#undef hm_VAL_TYPE
|
||||
#undef hm_KEY_EQUALSPROC
|
||||
#undef hm_KEY_HASHPROC
|
||||
#undef hm_KEY_TYPE
|
|
@ -352,6 +352,7 @@ m_Buddy_alloc(void *ctx, void *ptr, U64 old_size, U64 new_size) {
|
|||
if (found != NIL) {
|
||||
found->is_free = FALSE;
|
||||
U8 *p = (U8 *)found + b->alignment;
|
||||
m_memorySet(p, 0, new_size);
|
||||
if (ptr != NIL) {
|
||||
m_memoryCopy(p, ptr, old_size);
|
||||
}
|
||||
|
|
|
@ -203,4 +203,16 @@ S8_join(sl_S8 s, S8 join, m_Allocator *perm) {
|
|||
return (S8){total_length, mem};
|
||||
}
|
||||
|
||||
// Should probably get a better hashing func, but oh well
|
||||
FUNCTION U64
|
||||
S8_hash_prime19(S8 s)
|
||||
{
|
||||
U64 h = 0x100;
|
||||
for (U64 i = 0; i < s.len; i++) {
|
||||
h ^= s.raw[i];
|
||||
h *= 1111111111111111111u;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
#endif//BASTD_STRING_C
|
|
@ -7,7 +7,7 @@ set debugflags=/nologo /W3 /MTd /Od /Zi /RTC1 /fsanitize=address /link /incremen
|
|||
|
||||
REM Uncomment one of these to run an example. Try it! They're great documentation.
|
||||
REM cl ..\bastd\examples\memory.c %debugflags%
|
||||
REM cl ..\bastd\examples\fib_slice.c %debugflags%
|
||||
REM cl ..\bastd\examples\data_structs.c %debugflags%
|
||||
REM cl ..\bastd\examples\string.c %debugflags%
|
||||
REM cl ..\bastd\examples\buffer.c %debugflags%
|
||||
REM cl ..\bastd\examples\job_random.c %debugflags%
|
||||
|
|
Loading…
Add table
Reference in a new issue