Implemented bucket memory structure for custom allocators

This commit is contained in:
2024-09-07 19:42:50 +10:00
parent 023cf9c8b5
commit 81417e7f32
13 changed files with 275 additions and 44 deletions

View File

@@ -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" 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 a "value" can be of any valid datatype, and may point to various parts of memory to define it's value

View File

@@ -17,7 +17,7 @@ all: clean tests
.PHONY: tests .PHONY: tests
tests: tests:
$(MAKE) -C tests $(MAKE) -C tests -k
#util targets #util targets
$(TOY_OUTDIR): $(TOY_OUTDIR):

2
source/toy_ast.c Normal file
View File

@@ -0,0 +1,2 @@
#include "toy_ast.h"

9
source/toy_ast.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include "toy_common.h"
typedef enum Toy_AstType {
TOY_AST_PASS,
TOY_AST_ERROR,
} Toy_AstType;

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -23,7 +23,7 @@ const Toy_KeywordTypeTuple Toy_private_keywords[] = {
{TOY_TOKEN_KEYWORD_ASSERT, "assert"}, {TOY_TOKEN_KEYWORD_ASSERT, "assert"},
{TOY_TOKEN_KEYWORD_BREAK, "break"}, {TOY_TOKEN_KEYWORD_BREAK, "break"},
{TOY_TOKEN_KEYWORD_CLASS, "class"}, {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_CONTINUE, "continue"},
{TOY_TOKEN_KEYWORD_DO, "do"}, {TOY_TOKEN_KEYWORD_DO, "do"},
{TOY_TOKEN_KEYWORD_ELSE, "else"}, {TOY_TOKEN_KEYWORD_ELSE, "else"},

View File

@@ -19,4 +19,69 @@ void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
} }
return result; return result;
} }
//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);
}
}

View File

@@ -2,13 +2,45 @@
#include "toy_common.h" #include "toy_common.h"
//standard movable array for general use
#define TOY_GROW_CAPACITY(capacity) \ #define TOY_GROW_CAPACITY(capacity) \
((capacity) < 8 ? 8 : (capacity) * 2) ((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) \ #define TOY_GROW_ARRAY(type, pointer, oldSize, newSize) \
(type*)Toy_reallocate(pointer, sizeof(type)*oldSize, sizeof(type)*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) \ #define TOY_FREE_ARRAY(type, pointer, oldSize) \
(type*)Toy_reallocate(pointer, sizeof(type)*oldSize, 0) (type*)Toy_reallocate(pointer, sizeof(type)*oldSize, 0)
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize); 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))

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
typedef enum Toy_OpcodeType { typedef enum Toy_OpcodeType {
TOY_OPCODE_RETURN, TOY_OPCODE_PASS,
TOY_OPCODE_ERROR,
TOY_OPCODE_EOF,
} Toy_OpcodeType; } Toy_OpcodeType;

View File

@@ -14,6 +14,7 @@ typedef enum Toy_ValueType {
TOY_VALUE_OPAQUE, TOY_VALUE_OPAQUE,
} Toy_ValueType; } Toy_ValueType;
//4 bytes in size
typedef struct Toy_Value { typedef struct Toy_Value {
union { union {
bool boolean; //1 bool boolean; //1
@@ -26,7 +27,7 @@ typedef struct Toy_Value {
//TODO: opaque //TODO: opaque
} as; //4 } as; //4
Toy_ValueType type; //4 bytes Toy_ValueType type; //4
} Toy_Value; } Toy_Value;
#define TOY_VALUE_IS_NULL(value) ((value).type == TOY_VALUE_NULL) #define TOY_VALUE_IS_NULL(value) ((value).type == TOY_VALUE_NULL)

157
tests/cases/test_memory.c Normal file
View File

@@ -0,0 +1,157 @@
#include "toy_memory.h"
#include "toy_console_colors.h"
#include <stdio.h>
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;
}

View File

@@ -1,7 +1,6 @@
#include "toy_value.h" #include "toy_value.h"
#include "toy_console_colors.h" #include "toy_console_colors.h"
#include <assert.h>
#include <stdio.h> #include <stdio.h>
int main() { int main() {