Add memory pool test, fix bugs
This commit is contained in:
@@ -53,6 +53,9 @@ jobs:
|
|||||||
echo "UID=$(id -u)" >> "$GITHUB_ENV"
|
echo "UID=$(id -u)" >> "$GITHUB_ENV"
|
||||||
echo "GID=$(id -g)" >> "$GITHUB_ENV"
|
echo "GID=$(id -g)" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: cd tests && ./run_tests.sh
|
||||||
|
|
||||||
- name: Build the project
|
- name: Build the project
|
||||||
run: docker compose -f docker-compose.yml run --rm balatro sh -c "make -j$(nproc) && ./scripts/get_memory_map.sh"
|
run: docker compose -f docker-compose.yml run --rm balatro sh -c "make -j$(nproc) && ./scripts/get_memory_map.sh"
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
*.sln
|
*.sln
|
||||||
*.vcxproj*
|
*.vcxproj*
|
||||||
/balatro-gba/x64/**
|
/balatro-gba/x64/**
|
||||||
|
/tests/pool/build
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ LIBTONC := $(DEVKITPRO)/libtonc
|
|||||||
# the makefile is found
|
# the makefile is found
|
||||||
#
|
#
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source
|
SOURCES := source
|
||||||
INCLUDES := include
|
INCLUDES := include
|
||||||
DATA :=
|
DATA :=
|
||||||
MUSIC := audio
|
MUSIC := audio
|
||||||
GRAPHICS := graphics
|
GRAPHICS := graphics
|
||||||
|
POOLS_DEF_FILE := \"pools.def\"
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
@@ -42,7 +43,8 @@ GIT_DIRTY := $(shell git diff-index --quiet HEAD -- || echo "-dirty")
|
|||||||
GIT_HASH := $(shell git rev-parse --short HEAD || echo "undef")
|
GIT_HASH := $(shell git rev-parse --short HEAD || echo "undef")
|
||||||
GIT_C_FLAGS := -DGIT_HASH=\"$(GIT_HASH)\" -DGIT_DIRTY=\"$(GIT_DIRTY)\"
|
GIT_C_FLAGS := -DGIT_HASH=\"$(GIT_HASH)\" -DGIT_DIRTY=\"$(GIT_DIRTY)\"
|
||||||
|
|
||||||
CFLAGS := -g -O3 -Wall -Werror\
|
CFLAGS := -g -O3 -Wall -Werror \
|
||||||
|
-DPOOLS_DEF_FILE=$(POOLS_DEF_FILE) \
|
||||||
-mcpu=arm7tdmi -mtune=arm7tdmi \
|
-mcpu=arm7tdmi -mtune=arm7tdmi \
|
||||||
-ffast-math -fomit-frame-pointer -funroll-loops \
|
-ffast-math -fomit-frame-pointer -funroll-loops \
|
||||||
$(ARCH)
|
$(ARCH)
|
||||||
|
|||||||
+9
-9
@@ -1,16 +1,16 @@
|
|||||||
#ifndef POOL_H
|
#ifndef POOL_H
|
||||||
#define POOL_H
|
#define POOL_H
|
||||||
|
|
||||||
#include <tonc.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define POOL_BITS_PER_WORD 32
|
#define POOL_BITS_PER_WORD 32
|
||||||
#define POOL_MAX_CAPACITY 128
|
#define POOL_BITMAP_BYTES 8
|
||||||
|
|
||||||
typedef struct PoolBitmap {
|
typedef struct PoolBitmap {
|
||||||
u32 *w;
|
uint32_t *w;
|
||||||
u32 nbits;
|
uint32_t nbits;
|
||||||
u32 nwords;
|
uint32_t nwords;
|
||||||
u32 cap;
|
uint32_t cap;
|
||||||
} PoolBitmap;
|
} PoolBitmap;
|
||||||
|
|
||||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx);
|
void pool_bm_clear_idx(PoolBitmap *bm, int idx);
|
||||||
@@ -27,13 +27,13 @@ int pool_bm_get_free_idx(PoolBitmap *bm);
|
|||||||
|
|
||||||
#define POOL_DEFINE_TYPE(type, capacity) \
|
#define POOL_DEFINE_TYPE(type, capacity) \
|
||||||
static type type##_storage[capacity]; \
|
static type type##_storage[capacity]; \
|
||||||
static u32 type##_bitmap_w[sizeof(u32)] = {0}; \
|
static uint32_t type##_bitmap_w[POOL_BITMAP_BYTES] = {0}; \
|
||||||
static type##Pool type##_pool = \
|
static type##Pool type##_pool = \
|
||||||
{ \
|
{ \
|
||||||
.bm = { \
|
.bm = { \
|
||||||
.w = type##_bitmap_w, \
|
.w = type##_bitmap_w, \
|
||||||
.nbits = POOL_BITS_PER_WORD, \
|
.nbits = POOL_BITS_PER_WORD, \
|
||||||
.nwords = sizeof(u32), \
|
.nwords = POOL_BITMAP_BYTES, \
|
||||||
.cap = capacity, \
|
.cap = capacity, \
|
||||||
}, \
|
}, \
|
||||||
.objects = type##_storage, \
|
.objects = type##_storage, \
|
||||||
@@ -56,7 +56,7 @@ int pool_bm_get_free_idx(PoolBitmap *bm);
|
|||||||
|
|
||||||
#define POOL_ENTRY(name, capacity) \
|
#define POOL_ENTRY(name, capacity) \
|
||||||
POOL_DECLARE_TYPE(name);
|
POOL_DECLARE_TYPE(name);
|
||||||
#include "pools.def"
|
#include POOLS_DEF_FILE
|
||||||
#undef POOL_ENTRY
|
#undef POOL_ENTRY
|
||||||
|
|
||||||
#endif // POOL_H
|
#endif // POOL_H
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ for name in $(get_pool_names); do
|
|||||||
pool_size="$(cut -d ' ' -f 3 <<< $output_pool)"
|
pool_size="$(cut -d ' ' -f 3 <<< $output_pool)"
|
||||||
func_size="$(cut -d ' ' -f 3 <<< $output_func)"
|
func_size="$(cut -d ' ' -f 3 <<< $output_func)"
|
||||||
bm_size="$(cut -d ' ' -f 3 <<< $output_bm)"
|
bm_size="$(cut -d ' ' -f 3 <<< $output_bm)"
|
||||||
#bm_size=16 #always gonna be 16, 4 * sizeof(u32)
|
|
||||||
|
|
||||||
TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bm_size ))
|
TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bm_size ))
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -3,17 +3,17 @@
|
|||||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx)
|
void pool_bm_clear_idx(PoolBitmap *bm, int idx)
|
||||||
{
|
{
|
||||||
// Divide by 32 to get the word index
|
// Divide by 32 to get the word index
|
||||||
u32 i = idx >> 5;
|
uint32_t i = idx >> 5;
|
||||||
// Get last 5-bits, same as a modulo (% 32) operation on positive numbers
|
// Get last 5-bits, same as a modulo (% 32) operation on positive numbers
|
||||||
u32 b = idx & 0x1F;
|
uint32_t b = idx & 0x1F;
|
||||||
bm->w[i] &= ~((u32)1 << b);
|
bm->w[i] &= ~((uint32_t)1 << b);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pool_bm_get_free_idx(PoolBitmap *bm)
|
int pool_bm_get_free_idx(PoolBitmap *bm)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < bm->nwords; i++)
|
for (uint32_t i = 0; i < bm->nwords; i++)
|
||||||
{
|
{
|
||||||
u32 inv = ~bm->w[i];
|
uint32_t inv = ~bm->w[i];
|
||||||
|
|
||||||
// guard so we don't call `ctz` with 0, since __builtin_ctz(0) is undefined
|
// guard so we don't call `ctz` with 0, since __builtin_ctz(0) is undefined
|
||||||
// https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
|
// https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
|
||||||
@@ -26,7 +26,7 @@ int pool_bm_get_free_idx(PoolBitmap *bm)
|
|||||||
if (inv)
|
if (inv)
|
||||||
{
|
{
|
||||||
int bit = __builtin_ctz(inv);
|
int bit = __builtin_ctz(inv);
|
||||||
bm->w[i] |= ((u32)1 << bit);
|
bm->w[i] |= ((uint32_t)1 << bit);
|
||||||
int idx = i * POOL_BITS_PER_WORD + bit;
|
int idx = i * POOL_BITS_PER_WORD + bit;
|
||||||
return (idx < bm->cap) ? idx : -1;
|
return (idx < bm->cap) ? idx : -1;
|
||||||
}
|
}
|
||||||
@@ -38,5 +38,5 @@ int pool_bm_get_free_idx(PoolBitmap *bm)
|
|||||||
|
|
||||||
#define POOL_ENTRY(name, capacity) \
|
#define POOL_ENTRY(name, capacity) \
|
||||||
POOL_DEFINE_TYPE(name, capacity);
|
POOL_DEFINE_TYPE(name, capacity);
|
||||||
#include "pools.def"
|
#include POOLS_DEF_FILE
|
||||||
#undef POOL_ENTRY
|
#undef POOL_ENTRY
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
POOLS_DEF_FILE := \"test_pools.def\"
|
||||||
|
|
||||||
|
CC := gcc
|
||||||
|
CFLAGS := -I../../include -I. \
|
||||||
|
-g -O3 -Wall -Werror -DPOOLS_DEF_FILE=$(POOLS_DEF_FILE)
|
||||||
|
|
||||||
|
SRC := pool_test.c ../../source/pool.c
|
||||||
|
OUT := build/pool_test
|
||||||
|
|
||||||
|
$(OUT): $(SRC) | build
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#include "pool.h"
|
||||||
|
#include "test_structures.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
int get_random(int low, int high)
|
||||||
|
{
|
||||||
|
srand(time(NULL));
|
||||||
|
int n = low + rand() % (high - low + 1);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_fill(ChunkOfData* myPtrs[], int check_size)
|
||||||
|
{
|
||||||
|
int itr = 0;
|
||||||
|
|
||||||
|
ChunkOfData* test_chunk = NULL;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
test_chunk = POOL_GET(ChunkOfData);
|
||||||
|
if(test_chunk != NULL) myPtrs[itr++] = test_chunk;
|
||||||
|
} while(test_chunk != NULL);
|
||||||
|
|
||||||
|
if(itr != check_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: failed to get expected number of valid pointers\n"
|
||||||
|
" expected: %d, actual %d\n", check_size, itr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_fill_and_empty(void)
|
||||||
|
{
|
||||||
|
ChunkOfData* myPtrs[TEST_SIZE];
|
||||||
|
if(!test_fill(myPtrs, TEST_SIZE)) return false;
|
||||||
|
|
||||||
|
for(int itr = TEST_SIZE; itr >= 0; --itr)
|
||||||
|
{
|
||||||
|
POOL_FREE(ChunkOfData, myPtrs[itr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_fill_and_remove_at_random_and_refill_and_empty(void)
|
||||||
|
{
|
||||||
|
ChunkOfData* myPtrs[TEST_SIZE];
|
||||||
|
if(!test_fill(myPtrs, TEST_SIZE)) return false;
|
||||||
|
|
||||||
|
// remove between 10 and TEST_SIZE
|
||||||
|
int number_to_remove = get_random(100, TEST_SIZE);
|
||||||
|
|
||||||
|
for(int itr = 0; itr < number_to_remove; itr++)
|
||||||
|
{
|
||||||
|
POOL_FREE(ChunkOfData, myPtrs[itr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!test_fill(myPtrs, number_to_remove)) return false;
|
||||||
|
|
||||||
|
for(int itr = 0; itr < TEST_SIZE; itr++)
|
||||||
|
{
|
||||||
|
POOL_FREE(ChunkOfData, myPtrs[itr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// Test it twice to make sure empty works, kinda hacky.
|
||||||
|
if(!test_fill_and_empty()) return -1;
|
||||||
|
if(!test_fill_and_empty()) return -1;
|
||||||
|
|
||||||
|
// Similarly here, verify that fill, random num removal, refill, and empty
|
||||||
|
// by refilling and emptying again
|
||||||
|
if(!test_fill_and_remove_at_random_and_refill_and_empty()) return -1;
|
||||||
|
if(!test_fill_and_empty()) return -1;
|
||||||
|
|
||||||
|
printf("Pool Tests Passed\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include "test_structures.h"
|
||||||
|
|
||||||
|
#define TEST_SIZE 240
|
||||||
|
|
||||||
|
POOL_ENTRY(ChunkOfData, TEST_SIZE);
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef POOL_TEST_STRUCTURES
|
||||||
|
#define POOL_TEST_STRUCTURES
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int my_int;
|
||||||
|
float my_float;
|
||||||
|
char my_char;
|
||||||
|
} GenericData;
|
||||||
|
|
||||||
|
int my_type;
|
||||||
|
} ChunkOfData;
|
||||||
|
|
||||||
|
#endif // POOL_TEST_STRUCTURES
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
run_pool_test() {
|
||||||
|
cd pool
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
./build/pool_test
|
||||||
|
cd - > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pool_test
|
||||||
Reference in New Issue
Block a user