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.
|
operations are included by default.
|
||||||
- [x] Generic data-structures. The premier one is the
|
- [x] Generic data-structures. The premier one is the
|
||||||
[Dynamic Array](https://dylanfalconer.com/articles/dynamic-arrays-in-c), which
|
[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
|
- [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
|
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
|
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 3D Math API with supports for Vectors and Matricies using SIMD.
|
||||||
- [ ] A simple GUI initialization system that gets you a window with immediate-mode
|
- [ ] 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.
|
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.
|
- [ ] 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) {
|
if (found != NIL) {
|
||||||
found->is_free = FALSE;
|
found->is_free = FALSE;
|
||||||
U8 *p = (U8 *)found + b->alignment;
|
U8 *p = (U8 *)found + b->alignment;
|
||||||
|
m_memorySet(p, 0, new_size);
|
||||||
if (ptr != NIL) {
|
if (ptr != NIL) {
|
||||||
m_memoryCopy(p, ptr, old_size);
|
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};
|
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
|
#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 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\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\string.c %debugflags%
|
||||||
REM cl ..\bastd\examples\buffer.c %debugflags%
|
REM cl ..\bastd\examples\buffer.c %debugflags%
|
||||||
REM cl ..\bastd\examples\job_random.c %debugflags%
|
REM cl ..\bastd\examples\job_random.c %debugflags%
|
||||||
|
|
Loading…
Add table
Reference in a new issue