#include "base_core.h" #include "arena.h" #include "mini_arena.h" #include "platform.h" #include "pool.h" #include "string.h" #include #include void arena_dump(const Arena *arena) { return; MemoryBlock *block = arena->current; puts("@@@@@@@@@@"); while (block) { puts("-----Arena-----"); printf("base: %p\n", block->base); printf("size: %u\n", block->size); printf("first_free: %u\n", block->first_free); printf("total_used: %lu\n",block->arena_offset); printf("prev: %p\n", block->prev); puts("-----Arena end-"); block = block->prev; } } static void arena_test_1(void) { TRACEF("\n"); ArenaOptions arena_params = {}; U32 arena_size = MB(1) + MemoryBlock_Header_Size; U32 commit_size = KB(8); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); arena_dump(&arena); void *ptr = arena_push(&arena, 512, 1); MemoryZero(ptr, 512); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); ptr = arena_push(&arena, 512, 1); MemoryZero(ptr, 512); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); ptr = arena_push(&arena, 1, 1); MemoryZero(ptr, 1); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); arena_free(&arena); } static void arena_test_2(void) { TRACEF("\n"); ArenaOptions arena_params = {}; U32 arena_size = MB(3) + MemoryBlock_Header_Size; U32 commit_size = KB(72); U32 push_size = KB(32); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); void *ptr; ptr = arena_push(&arena, push_size, 8); MemoryZero(ptr, push_size); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); ATemp checkpoint = arena_temp_begin(&arena); ptr = arena_push(&arena, MB(4)+3, 8); MemoryZero(ptr, MB(2)); ptr = arena_push(&arena, commit_size * 3, 8); MemoryZero(ptr, commit_size * 3 ); ptr = arena_push(&arena, commit_size , 8); MemoryZero(ptr, commit_size ); ATemp checkpoint2 = arena_temp_begin(&arena); ptr = arena_push(&arena, 64, 8); MemoryZero(ptr, 64); arena_temp_end(&checkpoint2); arena_temp_end(&checkpoint); arena_reset_to(&arena, 1088); arena_dump(&arena); ptr = arena_push(&arena, 1, KB(16)); ((U8*)ptr)[0] = 0x37; ptr = arena_push(&arena, 1, KB(16)); ((U8*)ptr)[0] = 0x73; ptr = arena_push(&arena, 0, KB(4)); //((U8*)ptr)[0] = 0x73; // arena_reset_to(&arena, 1089); arena_dump(&arena); arena_reset_to(&arena, 0); for (U64 i = 0; i < MB(4); i++) { ptr = arena_push(&arena, 3, 1); MemoryZero(ptr, 3); } arena_free(&arena); } static void arena_test_3(void) { TRACEF("\n"); ArenaOptions arena_params = {}; U32 arena_size = KB(97) + MemoryBlock_Header_Size; U32 commit_size = KB(97); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); arena_dump(&arena); void *ptr = arena_push(&arena, KB(64), 32); MemoryZero(ptr, KB(64)); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); ptr = arena_push(&arena, KB(97) - KB(64), 32); MemoryZero(ptr, KB(97) - KB(64)); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); arena_free(&arena); } // Exercises the alignment-padding edge case: a fresh block has first_free at // MemoryBlock_Header_Size (64), so a large alignment pushes the user pointer // well past the header. Requesting (block_size - 64) bytes with page alignment // previously tripped the end_pos <= block->size assert because the alignment // gap was not accounted for. After the fix this should allocate a larger block. static void arena_test_4(void) { TRACEF("\n"); U32 page_size = platform_page_size(); ArenaOptions arena_params = {}; U32 arena_size = MB(1) + MemoryBlock_Header_Size; U32 commit_size = KB(64); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); // block_size is the page-aligned arena_size. Request nearly the whole block // but with page alignment, so the alignment gap (a full page) forces a grow. U32 block_size = arena.block_size; U32 push_size = block_size - MemoryBlock_Header_Size; void *ptr = arena_push(&arena, push_size, page_size); Assert(IsAlignedTo((uintptr_t)ptr, page_size)); MemoryZero(ptr, push_size); printf("test_4: allocated %u bytes page-aligned at %p\n", push_size, ptr); arena_dump(&arena); // A second page-aligned push to make sure the cursor math still holds. ptr = arena_push(&arena, page_size, page_size); Assert(IsAlignedTo((uintptr_t)ptr, page_size)); MemoryZero(ptr, page_size); printf("test_4: allocated %u bytes page-aligned at %p\n", page_size, ptr); arena_free(&arena); } static void arena_test_allocator_interface(void) { TRACEF("\n"); U32 page_size = platform_page_size(); ArenaOptions arena_params = {}; U32 arena_size = MB(1) + MemoryBlock_Header_Size; U32 commit_size = KB(64); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); Allocator allocator = arena_allocator(&arena); // block_size is the page-aligned arena_size. Request nearly the whole block // but with page alignment, so the alignment gap (a full page) forces a grow. U32 block_size = arena.block_size; U32 push_size = block_size - MemoryBlock_Header_Size; void *ptr = allocator_push(&allocator, push_size, page_size); Assert(IsAlignedTo((uintptr_t)ptr, page_size)); MemoryZero(ptr, push_size); printf("test_4: allocated %u bytes page-aligned at %p\n", push_size, ptr); arena_dump(&arena); // A second page-aligned push to make sure the cursor math still holds. ptr = allocator_push(&allocator, page_size, page_size); Assert(IsAlignedTo((uintptr_t)ptr, page_size)); MemoryZero(ptr, page_size); printf("test_4: allocated %u bytes page-aligned at %p\n", page_size, ptr); allocator_free_all(&allocator); arena_free(&arena); } static void arena_test_decommit(void) { TRACEF("\n"); U32 page_size = platform_page_size(); ArenaOptions arena_params = {}; U32 arena_size = MB(1) + MemoryBlock_Header_Size; U32 commit_size = KB(64); arena_params.size = arena_size; arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); void *ptr = arena_push(&arena, commit_size * 10, 8); MemoryZero(ptr, commit_size * 10); arena_reset(&arena); void *pre_ptr = arena_push(&arena, 5, 4); ATemp tmp = arena_temp_begin(&arena); ptr = arena_push(&arena, commit_size * 2 + 13, 8); MemoryZero(ptr, commit_size * 2 + 13); arena_temp_end(&tmp); MemoryZero(pre_ptr, 5); arena_free(&arena); } void miniarena_test1(void) { TRACEF("\n"); #define arena_size 4096 MARENA_DEFINE(arena, arena_size, 8); int i; for (i = 0; ; i++) { void *ptr = marena_push(&arena, 1); if (ptr == NULL) { break; } } printf("miniarena_test1,size %u pushed %d bytes first_free %u\n", arena.size, i, arena.first_free); marena_reset(&arena); MTemp checkpoint = marena_temp_begin(&arena); void *ptr = marena_push(&arena, arena_size); Assert(ptr != NULL); Assert(arena.first_free == arena_size); MemoryZero(ptr, arena_size); marena_temp_end(&checkpoint); Assert(arena.first_free == 0); U32 *val = marena_push_type(&arena, U32); *val = 0xa0b0c0d0; printf("marena val 0x%x\n", *val); printf("miniarena_test1,size %u bytes first_free %u\n", arena.size, arena.first_free); Assert(arena.first_free == sizeof(U32)); } typedef struct PoolTest PoolTest; struct PoolTest{ U32 n; PoolTest *next; char name[12]; }; void apool_test(void) { TRACEF("\n"); Arena arena; arena_init(&arena, &ArenaOptions_Default); Allocator allocator = arena_allocator(&arena); APool *pool = apool_create_ex(&allocator, 16, sizeof(PoolTest), alignof(PoolTest)); PoolTest *list = NULL; for (U32 i = 0; i < U32_Max; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } PoolTest *next; U32 count = 0; for (PoolTest *p = list; count < 7 && p; p = next){ count++; next = p->next; printf("Pooltest freeing %u\n",p->n); apool_free(pool, p); } list = NULL; for (U32 i = 0; i < U32_Max; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } allocator_free_all(&allocator); arena_free(&arena); } void apool_test_miniarena(void) { TRACEF("\n"); MArena arena; U8 data[4096]; marena_init(&arena, data, sizeof data); Allocator allocator = marena_allocator(&arena); APool *pool = apool_create_ex(&allocator, 16, sizeof(PoolTest), alignof(PoolTest)); PoolTest *list = NULL; for (U32 i = 0; i < U32_Max; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } PoolTest *next; U32 count = 0; for (PoolTest *p = list; count < 7 && p; p = next){ count++; next = p->next; printf("Pooltest freeing %u\n",p->n); apool_free(pool, p); } list = NULL; for (U32 i = 0; i < U32_Max; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } allocator_free_all(&allocator); } void apool_test_autogrow(void) { TRACEF("\n"); Arena arena; arena_init(&arena, &ArenaOptions_Default); Allocator allocator = arena_allocator(&arena); APool *pool = apool_create(&allocator, APOOL_AUTOGROW, PoolTest); PoolTest *list = NULL; for (U32 i = 0; i < 4; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } PoolTest *next; U32 count = 0; for (PoolTest *p = list; count < 3 && p; p = next){ count++; next = p->next; printf("Pooltest freeing %u\n",p->n); apool_free(pool, p); } list = NULL; for (U32 i = 0; i < 5; i++) { PoolTest *pt = apool_alloc(pool); if (pt == NULL){ break; } pt->n = i; pt->next = list; list = pt; } for (PoolTest *p = list; p; p = p->next){ printf("Pooltest %u\n",p->n); } allocator_free_all(&allocator); arena_free(&arena); } void string_test_1(void) { TRACEF("\n"); Str str1 = S("Hello"); printf("str1: %.*s len %d\n", str1.len, str1.str, str1.len); U64 str1_index_of_l = str_find_byte(str1, 'l'); U64 str1_index_of_g = str_find_byte(str1, 'g'); printf("str1: index of l %ld, index of g: %ld\n", str1_index_of_l, str1_index_of_g); MARENA_DEFINE(arena, 512, 1); Allocator alloc = marena_allocator(&arena); Str str2 = str_dup(&alloc, S(" World")); Str str3 = str_concat(&alloc, str1, str2); printf("str3: %.*s len %d\n", str3.len, str3.str, str3.len); } void string_split_test_1(void) { TRACEF("\n"); Str str1 = S("|a:bb|ccc|"); Str sep = S("|:"); STRVEC_DEFINE(strings, 16); str_split(str1, sep, &strings); for (U32 i = 0; i < strings.len; i++) { Str *str = &strings.strings[i]; printf("split %u: '%.*s'\n", i, str->len, str->str); } } void string_test_misc(void) { TRACEF("\n"); Str str1 = S("Hello World"); bool prefix1 = str_starts_with(str1, S("He")); bool prefix2 = str_starts_with(str1, S("")); bool prefix3 = str_starts_with(str1, S("false")); bool prefix4 = str_starts_with(str1, S("Hello World")); bool suffix1 = str_ends_with(str1, S("orld")); bool suffix2 = str_ends_with(str1, S("")); bool suffix3 = str_ends_with(str1, S("false")); bool suffix4 = str_ends_with(str1, S("Hello World")); printf("matches: %d %d %d %d %d %d %d %d\n", prefix1, prefix2, prefix3, prefix4, suffix1, suffix2, suffix3, suffix4); Str str2 = S(" Hello World\t \n"); Str str2_trim = str_ascii_trim(str2); printf("str2_trim: '%.*s'\n", str2_trim.len, str2_trim.str); printf("str2_trim == str1: %d\n", str_eq(str1, str2_trim)); Str str3 = str_empty(); Str str3_trim = str_ascii_trim(str3); printf("str3_trim: '%.*s'\n", str3_trim.len, str3_trim.str); Str str4 = S(" \t\t "); Str str4_trim = str_ascii_trim(str4); printf("str4_trim: '%.*s'\n", str4_trim.len, str4_trim.str); S64 found = str_index(str1, S("World")); printf("1. found %ld\n", found); found = str_index(str_empty(), S("World")); printf("2. found %ld\n", found); found = str_index(str_empty(), S("")); printf("3. found %ld\n", found); found = str_index(str1, S(" ")); printf("4. found %ld\n", found); } void string_test_join(void) { TRACEF("\n"); Arena arena; arena_init(&arena, &ArenaOptions_Default); Allocator allocator = arena_allocator(&arena); Str strs[3] = {S("1"), S("two"), S("3-3")}; StrVec vec = STRVEC_STATIC_INITIALIZER(strs); Str str = str_join(&allocator, vec, S("/")); printf("str_join 1 '%.*s'\n", str.len, str.str); str = str_join(&allocator, vec, S("")); printf("str_join 2 '%.*s'\n", str.len, str.str); vec.len = 0; str = str_join(&allocator, vec, S("/")); printf("str_join 2 '%.*s'\n", str.len, str.str); } void print_runes(Str str) { U32 offset = 0; U32 len = str_rune_count(str); printf("rune len %d\n", len); while (offset < str.len) { RuneDecode d = str_utf8_decode(str, offset); printf("Rune: %d valid: %d\n", d.rune, d.valid); offset += d.width; } } void string_test_utf8(void) { TRACEF("\n"); Str str1 = S("Test 1ø2 ☺"); TRACEF("str1:\n"); print_runes(str1); Str str2 = str_empty(); TRACEF("str2:\n"); print_runes(str2); Str str3 = S("1Å 2\xc6" "3\xc3"); TRACEF("str3:\n"); print_runes(str3); const char rune_error[] = {0xEF, 0xBF, 0xBD, 0x00} ; // RUNE_ERROR encoded in utf-8 Str str4 = S(rune_error); TRACEF("str4:\n"); print_runes(str4); // this should be a valid code point } void string_test_hex(void) { TRACEF("\n"); MArena arena; U8 data[4096]; marena_init(&arena, data, sizeof data); Allocator allocator = marena_allocator(&arena); Str str1 = S("Hello World ☺"); Str str1_encoded = str_hex_encode_alloc(&allocator, str1); printf("str1_encoded: '%.*s'\n", str1_encoded.len, str1_encoded.str); Str str1_decoded = str_hex_decode_alloc(&allocator, str1_encoded); printf("str1_decoded: '%.*s'\n", str1_decoded.len, str1_decoded.str); Str str2 = S(""); Str str2_encoded = str_hex_encode_alloc(&allocator, str2); printf("str2_encoded: '%.*s'\n", str2_encoded.len, str2_encoded.str); Str str2_decoded = str_hex_decode_alloc(&allocator, str2_encoded); printf("str2_decoded: '%.*s'\n", str2_decoded.len, str2_decoded.str); } typedef struct StrSLNode StrSLNode; struct StrSLNode { Str str; StrSLNode *next; }; typedef struct StrSList StrSList; struct StrSList { StrSLNode *first; StrSLNode **last; }; void string_test_slist(void) { TRACEF("\n"); Str str1 = S("one"); Str str2 = S("two"); Str str3 = S("three"); StrSList strlist = {}; StrSLNode n1 = {str1, NULL}; StrSLNode n2 = {str2, NULL}; StrSLNode n3 = {str3, NULL}; SListInit(&strlist); SListAddFirst(&strlist, &n1); SListAddFirst(&strlist, &n2); SListAddFirst(&strlist, &n3); TRACEF("List test first\n"); for (StrSLNode *n = strlist.first; n != NULL; n = n->next) { printf("Node: %.*s\n", n->str.len, n->str.str); } TRACEF("List test first done\n"); SListInit(&strlist); SListAddLast(&strlist, &n1); SListAddLast(&strlist, &n2); SListAddLast(&strlist, &n3); SListRemoveFirst(&strlist); TRACEF("List test last\n"); for (StrSLNode *n = strlist.first; n != NULL; n = n->next) { printf("Node: %.*s\n", n->str.len, n->str.str); } TRACEF("List test last done\n"); } typedef struct StrDLNode StrDLNode; struct StrDLNode { Str str; StrDLNode *next; StrDLNode *prev; }; typedef struct StrDList StrDList; struct StrDList { StrDLNode *first; StrDLNode *last; }; void string_test_dlist(void) { TRACEF("\n"); Str str1 = S("one"); Str str2 = S("two"); Str str3 = S("three"); Str str4 = S("four"); StrDList strlist = {}; StrDLNode n1 = {str1, NULL, NULL}; StrDLNode n2 = {str2, NULL, NULL}; StrDLNode n3 = {str3, NULL, NULL}; StrDLNode n4 = {str4, NULL, NULL}; DListInit(&strlist); DListAddFirst(&strlist, &n1); DListAddFirst(&strlist, &n2); DListAddFirst(&strlist, &n3); TRACEF("List test first\n"); for (StrDLNode *n = strlist.first; n != NULL; n = n->next) { printf("Node: %.*s\n", n->str.len, n->str.str); } TRACEF("List test first done\n"); DListInit(&strlist); DListAddLast(&strlist, &n2); DListAddLast(&strlist, &n3); DListAddLast(&strlist, &n4); DListAddFirst(&strlist, &n1); TRACEF("List test full\n"); for (StrDLNode *n = strlist.first; n != NULL; n = n->next) { printf("Node: %.*s\n", n->str.len, n->str.str); } TRACEF("List test full done\n"); DListRemove(&strlist, &n1); DListRemoveFirst(&strlist); DListRemoveLast(&strlist); TRACEF("List test tail removed\n"); for (StrDLNode *n = strlist.first; n != NULL; n = n->next) { printf("Node: %.*s\n", n->str.len, n->str.str); } TRACEF("List test tail removed done\n"); } int main(int argc, char *argv[]) { arena_test_1(); arena_test_2(); arena_test_3(); arena_test_4(); arena_test_decommit(); arena_test_allocator_interface(); miniarena_test1(); apool_test(); apool_test_autogrow(); apool_test_miniarena(); string_test_1(); string_split_test_1(); string_test_misc(); string_test_join(); string_test_utf8(); string_test_hex(); string_test_slist(); string_test_dlist(); }