From 81417e7f32b66fe45ebde59def9df8ad4c86a7fe Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 7 Sep 2024 19:42:50 +1000 Subject: [PATCH] Implemented bucket memory structure for custom allocators --- .notes/SECD-concept.txt | 2 +- makefile | 2 +- source/toy_ast.c | 2 + source/toy_ast.h | 9 +++ source/toy_chunk.c | 24 ------ source/toy_chunk.h | 13 ---- source/toy_keywords.c | 2 +- source/toy_memory.c | 67 +++++++++++++++- source/toy_memory.h | 32 ++++++++ source/toy_opcodes.h | 5 +- source/toy_value.h | 3 +- tests/cases/test_memory.c | 157 ++++++++++++++++++++++++++++++++++++++ tests/cases/test_value.c | 1 - 13 files changed, 275 insertions(+), 44 deletions(-) create mode 100644 source/toy_ast.c create mode 100644 source/toy_ast.h delete mode 100644 source/toy_chunk.c delete mode 100644 source/toy_chunk.h create mode 100644 tests/cases/test_memory.c diff --git a/.notes/SECD-concept.txt b/.notes/SECD-concept.txt index 686fdf4..cbc4990 100644 --- a/.notes/SECD-concept.txt +++ b/.notes/SECD-concept.txt @@ -13,7 +13,7 @@ Notes: unlike version 1, identifiers are not a valid datatype - they're just an index representing a symbol, like "standard::clock" - placeholder opcodes - EOF, PASS, ERROR, + meta opcodes - EOF, PASS, ERROR, a "value" can be of any valid datatype, and may point to various parts of memory to define it's value diff --git a/makefile b/makefile index da5efd1..5afdc74 100644 --- a/makefile +++ b/makefile @@ -17,7 +17,7 @@ all: clean tests .PHONY: tests tests: - $(MAKE) -C tests + $(MAKE) -C tests -k #util targets $(TOY_OUTDIR): diff --git a/source/toy_ast.c b/source/toy_ast.c new file mode 100644 index 0000000..da59574 --- /dev/null +++ b/source/toy_ast.c @@ -0,0 +1,2 @@ +#include "toy_ast.h" + diff --git a/source/toy_ast.h b/source/toy_ast.h new file mode 100644 index 0000000..24ad45b --- /dev/null +++ b/source/toy_ast.h @@ -0,0 +1,9 @@ +#pragma once + +#include "toy_common.h" + +typedef enum Toy_AstType { + TOY_AST_PASS, + TOY_AST_ERROR, +} Toy_AstType; + diff --git a/source/toy_chunk.c b/source/toy_chunk.c deleted file mode 100644 index 288b559..0000000 --- a/source/toy_chunk.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "toy_chunk.h" - -#include "toy_memory.h" - -void Toy_initChunk(Toy_Chunk* chunk) { - chunk->count = 0; - chunk->capacity = 0; - chunk->code = NULL; -} - -void Toy_pushChunk(Toy_Chunk* chunk, uint8_t byte) { - if (chunk->count +1 > chunk->capacity) { - int oldCapacity = chunk->capacity; - chunk->capacity = TOY_GROW_CAPACITY(oldCapacity); - chunk->code = TOY_GROW_ARRAY(uint8_t, chunk->code, oldCapacity, chunk->capacity); - } - - chunk->code[chunk->count++] = byte; -} - -void Toy_freeChunk(Toy_Chunk* chunk) { - TOY_FREE_ARRAY(uint8_t, chunk->code, chunk->capacity); - Toy_initChunk(chunk); -} diff --git a/source/toy_chunk.h b/source/toy_chunk.h deleted file mode 100644 index ace9190..0000000 --- a/source/toy_chunk.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "toy_common.h" - -typedef struct Toy_Chunk { - int count; - int capacity; - uint8_t* code; -} Toy_Chunk; - -TOY_API void Toy_initChunk(Toy_Chunk* chunk); -TOY_API void Toy_pushChunk(Toy_Chunk* chunk, uint8_t byte); -TOY_API void Toy_freeChunk(Toy_Chunk* chunk); diff --git a/source/toy_keywords.c b/source/toy_keywords.c index 81b49ac..f185a2e 100644 --- a/source/toy_keywords.c +++ b/source/toy_keywords.c @@ -23,7 +23,7 @@ const Toy_KeywordTypeTuple Toy_private_keywords[] = { {TOY_TOKEN_KEYWORD_ASSERT, "assert"}, {TOY_TOKEN_KEYWORD_BREAK, "break"}, {TOY_TOKEN_KEYWORD_CLASS, "class"}, - {TOY_TOKEN_KEYWORD_CONST, "const"}, //TODO: investigate the constness of types + {TOY_TOKEN_KEYWORD_CONST, "const"}, {TOY_TOKEN_KEYWORD_CONTINUE, "continue"}, {TOY_TOKEN_KEYWORD_DO, "do"}, {TOY_TOKEN_KEYWORD_ELSE, "else"}, diff --git a/source/toy_memory.c b/source/toy_memory.c index ef1b2ff..e7b7eb5 100644 --- a/source/toy_memory.c +++ b/source/toy_memory.c @@ -19,4 +19,69 @@ void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) { } return result; -} \ No newline at end of file +} + +//buckets of fun +void Toy_initBucket(Toy_Bucket** bucketHandle, size_t capacity) { + if (capacity == 0) { + fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Cannot init a bucket with zero capacity\n" TOY_CC_RESET); + exit(1); + } + + (*bucketHandle) = malloc(sizeof(Toy_Bucket)); + + if ((*bucketHandle) == NULL) { + fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to allocate space for a bucket\n" TOY_CC_RESET); + exit(1); + } + + //initialize the bucket + (*bucketHandle)->next = NULL; + (*bucketHandle)->contents = NULL; //leave until the first partition + (*bucketHandle)->capacity = capacity; + (*bucketHandle)->count = 0; +} + +void* Toy_partBucket(Toy_Bucket** bucketHandle, size_t space) { + if ((*bucketHandle) == NULL) { + fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Expected bucket, received NULL\n" TOY_CC_RESET); + exit(1); + } + + //if out of space in the current bucket + if ((*bucketHandle)->capacity < (*bucketHandle)->count + space) { + //move to the next bucket + Toy_Bucket* tmp = NULL; + Toy_initBucket(&tmp, (*bucketHandle)->capacity); + tmp->next = (*bucketHandle); + (*bucketHandle) = tmp; + } + + //if no space allocated for the current bucket + if ((*bucketHandle)->contents == NULL) { + //allocate space for the current bucket + (*bucketHandle)->contents = malloc((*bucketHandle)->capacity); + + //double check + if ((*bucketHandle)->contents == NULL) { + fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to allocate space for bucket contents\n" TOY_CC_RESET); + exit(1); + } + } + + //track the new count, and return the specified memory space + (*bucketHandle)->count += space; + return ((*bucketHandle)->contents + (*bucketHandle)->count - space); +} + +void Toy_freeBucket(Toy_Bucket** bucketHandle) { + while ((*bucketHandle) != NULL) { + //run down the chain + Toy_Bucket* ptr = (*bucketHandle); + (*bucketHandle) = (*bucketHandle)->next; + + //clear the previous bucket from memory + free(ptr->contents); + free(ptr); + } +} diff --git a/source/toy_memory.h b/source/toy_memory.h index b3649bf..26d8381 100644 --- a/source/toy_memory.h +++ b/source/toy_memory.h @@ -2,13 +2,45 @@ #include "toy_common.h" +//standard movable array for general use #define TOY_GROW_CAPACITY(capacity) \ ((capacity) < 8 ? 8 : (capacity) * 2) +#define TOY_ALLOCATE(type, count) \ + (type*)Toy_reallocate(NULL, 0, sizeof(type)*(count)) + +#define TOY_FREE(type, pointer) \ + (type*)Toy_reallocate(pointer, sizeof(type), 0) + #define TOY_GROW_ARRAY(type, pointer, oldSize, newSize) \ (type*)Toy_reallocate(pointer, sizeof(type)*oldSize, sizeof(type)*newSize) +#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) \ + (type*)Toy_reallocate((type*)pointer, sizeof(type)*(oldCount), sizeof(type)*(count)) + #define TOY_FREE_ARRAY(type, pointer, oldSize) \ (type*)Toy_reallocate(pointer, sizeof(type)*oldSize, 0) TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize); + +//immobile "bucket" memory structure for custom allocators +typedef struct Toy_Bucket { + struct Toy_Bucket* next; + void* contents; + int capacity; + int count; +} Toy_Bucket; + +TOY_API void Toy_initBucket(Toy_Bucket** bucketHandle, size_t capacity); +TOY_API void* Toy_partBucket(Toy_Bucket** bucketHandle, size_t space); +TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle); + +#define TOY_BUCKET_INIT(type, bucket, capacity) \ + Toy_initBucket(&(bucket), sizeof(type)*(capacity)) + +#define TOY_BUCKET_PART(type, bucket) \ + Toy_partBucket(&(bucket), sizeof(type)) + +#define TOY_BUCKET_FREE(bucket) \ + Toy_freeBucket(&(bucket)) + diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index ce19e20..22d7ecb 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -1,5 +1,8 @@ #pragma once typedef enum Toy_OpcodeType { - TOY_OPCODE_RETURN, + TOY_OPCODE_PASS, + TOY_OPCODE_ERROR, + TOY_OPCODE_EOF, } Toy_OpcodeType; + diff --git a/source/toy_value.h b/source/toy_value.h index 04d711a..afacf39 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -14,6 +14,7 @@ typedef enum Toy_ValueType { TOY_VALUE_OPAQUE, } Toy_ValueType; +//4 bytes in size typedef struct Toy_Value { union { bool boolean; //1 @@ -26,7 +27,7 @@ typedef struct Toy_Value { //TODO: opaque } as; //4 - Toy_ValueType type; //4 bytes + Toy_ValueType type; //4 } Toy_Value; #define TOY_VALUE_IS_NULL(value) ((value).type == TOY_VALUE_NULL) diff --git a/tests/cases/test_memory.c b/tests/cases/test_memory.c new file mode 100644 index 0000000..431d8cb --- /dev/null +++ b/tests/cases/test_memory.c @@ -0,0 +1,157 @@ +#include "toy_memory.h" +#include "toy_console_colors.h" + +#include + +int test_reallocate() { + //test single pointer + { + int* integer = TOY_ALLOCATE(int, 1); + TOY_FREE(int, integer); + } + + //test single pointer array + { + int* array = TOY_ALLOCATE(int, 10); + + //check you can access the memory + array[1] = 42; + + TOY_FREE_ARRAY(int, array, 10); + } + + //test multiple pointer arrays + { + int* array1 = TOY_ALLOCATE(int, 10); + int* array2 = TOY_ALLOCATE(int, 10); + + array1[1] = 42; //access the given memory + array2[1] = 42; //access the given memory + + TOY_FREE_ARRAY(int, array1, 10); + TOY_FREE_ARRAY(int, array2, 10); + } + + return 0; +} + +int test_buckets() { + //test initializing and freeing a bucket + { + //init + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(int, bucket, 32); + + //check + if (bucket == NULL || bucket->capacity != 32 * sizeof(int)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to initialize 'Toy_Bucket'\n" TOY_CC_RESET); + return -1; + } + + //cleanup + TOY_BUCKET_FREE(bucket); + } + + //test partitioning a bucket, several times + { + //init + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(int, bucket, 32); + + //grab some memory + int* a = TOY_BUCKET_PART(int, bucket); + int* b = TOY_BUCKET_PART(int, bucket); + int* c = TOY_BUCKET_PART(int, bucket); + int* d = TOY_BUCKET_PART(int, bucket); + + //check + if (bucket == NULL || bucket->count != 4 * sizeof(int)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to partition 'Toy_Bucket' correctly: count is %d, expected %d\n" TOY_CC_RESET, bucket->count, (int)(4*sizeof(int))); + return -1; + } + + //cleanup + TOY_BUCKET_FREE(bucket); + } + + //test partitioning a bucket, several times, with an internal expansion + { + //init + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(int, bucket, 4); + + //grab some memory + int* a = TOY_BUCKET_PART(int, bucket); + int* b = TOY_BUCKET_PART(int, bucket); + int* c = TOY_BUCKET_PART(int, bucket); + int* d = TOY_BUCKET_PART(int, bucket); + int* e = TOY_BUCKET_PART(int, bucket); + int* f = TOY_BUCKET_PART(int, bucket); + + //checks - please note that the top-most bucket is what is being filled - older buckets are further along + if ( + bucket->capacity != 4 * sizeof(int) || + bucket->count != 2 * sizeof(int) || + bucket->next == NULL || + bucket->next->capacity != 4 * sizeof(int) || + bucket->next->count != 4 * sizeof(int)) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to expand 'Toy_Bucket' correctly\n" TOY_CC_RESET); + return -1; + } + + //cleanup + TOY_BUCKET_FREE(bucket); + } + + //test partitioning a bucket, several times, with an internal expansion, and awkward sizes + { + //init + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 32); + + //grab some memory + void* a = Toy_partBucket(&bucket, 16); + void* b = Toy_partBucket(&bucket, 10); + void* c = Toy_partBucket(&bucket, 10); + void* d = Toy_partBucket(&bucket, 10); + + //checks - awkward and mismatched sizes is not officially supported, but it should work + if ( + bucket->capacity != 32 || + bucket->count != 20 || + bucket->next == NULL || + bucket->next->capacity != 32 || + bucket->next->count != 26) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to expand 'Toy_Bucket' with awkward/mismatched sizes correctly\n" TOY_CC_RESET); + return -1; + } + + //cleanup + TOY_BUCKET_FREE(bucket); + } + + return 0; +} + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + res = test_reallocate(); + total += res; + + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + + res = test_buckets(); + total += res; + + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + + return total; +} diff --git a/tests/cases/test_value.c b/tests/cases/test_value.c index a48d389..8ccaf85 100644 --- a/tests/cases/test_value.c +++ b/tests/cases/test_value.c @@ -1,7 +1,6 @@ #include "toy_value.h" #include "toy_console_colors.h" -#include #include int main() {