diff --git a/README.md b/README.md index f9c43b6..d6d8772 100644 --- a/README.md +++ b/README.md @@ -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. --- diff --git a/bastd/examples/data_structs.c b/bastd/examples/data_structs.c new file mode 100644 index 0000000..f60d79c --- /dev/null +++ b/bastd/examples/data_structs.c @@ -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; +} \ No newline at end of file diff --git a/bastd/examples/fib_slice.c b/bastd/examples/fib_slice.c deleted file mode 100644 index cf722e9..0000000 --- a/bastd/examples/fib_slice.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/bastd/hashmap.c b/bastd/hashmap.c new file mode 100644 index 0000000..bd92886 --- /dev/null +++ b/bastd/hashmap.c @@ -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 \ No newline at end of file diff --git a/bastd/memory.c b/bastd/memory.c index eb03f2e..b05d5aa 100644 --- a/bastd/memory.c +++ b/bastd/memory.c @@ -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); } diff --git a/bastd/string.c b/bastd/string.c index 12e2753..3eee39d 100644 --- a/bastd/string.c +++ b/bastd/string.c @@ -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 \ No newline at end of file diff --git a/build.bat b/build.bat index 1050e9a..a695c9d 100644 --- a/build.bat +++ b/build.bat @@ -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%