Merge branch 'main' into start_music_after_splashscreen
This commit is contained in:
@@ -53,8 +53,11 @@ jobs:
|
||||
echo "UID=$(id -u)" >> "$GITHUB_ENV"
|
||||
echo "GID=$(id -g)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run Tests
|
||||
run: cd tests && ./run_tests.sh
|
||||
|
||||
- name: Build the project
|
||||
run: docker compose -f docker-compose.yml run --rm balatro sh -c "make -j$(nproc)"
|
||||
run: docker compose -f docker-compose.yml run --rm balatro sh -c "make -j$(nproc) && ./scripts/get_memory_map.sh"
|
||||
|
||||
- uses: softprops/action-gh-release@v2
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
*.sln
|
||||
*.vcxproj*
|
||||
/balatro-gba/x64/**
|
||||
/tests/pool/build
|
||||
|
||||
@@ -25,13 +25,14 @@ LIBTONC := $(DEVKITPRO)/libtonc
|
||||
# the makefile is found
|
||||
#
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include
|
||||
DATA :=
|
||||
MUSIC := audio
|
||||
GRAPHICS := graphics
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include
|
||||
DATA :=
|
||||
MUSIC := audio
|
||||
GRAPHICS := graphics
|
||||
POOLS_DEF_FILE := \"def_balatro_mempool.h\"
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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_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 \
|
||||
-ffast-math -fomit-frame-pointer -funroll-loops \
|
||||
$(ARCH)
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "sprite.h"
|
||||
|
||||
#define MAX_CARDS (NUM_SUITS * NUM_RANKS)
|
||||
#define MAX_CARDS_ON_SCREEN 16
|
||||
|
||||
#define CARD_TID 0
|
||||
#define CARD_SPRITE_OFFSET 16
|
||||
#define CARD_PB 0
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "sprite.h"
|
||||
#include "joker.h"
|
||||
#include "card.h"
|
||||
|
||||
POOL_ENTRY(Sprite, MAX_SPRITES);
|
||||
POOL_ENTRY(SpriteObject, MAX_SPRITE_OBJECTS);
|
||||
POOL_ENTRY(Joker, MAX_ACTIVE_JOKERS);
|
||||
POOL_ENTRY(JokerObject, MAX_ACTIVE_JOKERS);
|
||||
POOL_ENTRY(Card, MAX_CARDS);
|
||||
POOL_ENTRY(CardObject, MAX_CARDS_ON_SCREEN);
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "game.h"
|
||||
#include "graphic_utils.h"
|
||||
|
||||
// This won't be more than the number of jokers in your current deck
|
||||
// plus the amount that can fit in the shop, 8 should be fine. For now...
|
||||
#define MAX_ACTIVE_JOKERS 8
|
||||
|
||||
#define JOKER_TID (MAX_HAND_SIZE + MAX_SELECTION_SIZE) * JOKER_SPRITE_OFFSET // Tile ID for the starting index in the tile memory
|
||||
#define JOKER_SPRITE_OFFSET 16 // Offset for the joker sprites
|
||||
#define JOKER_BASE_PB 4 // The starting palette index for the jokers
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef POOL_H
|
||||
#define POOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define POOL_BITS_PER_WORD 32
|
||||
#define POOL_BITMAP_BYTES 8
|
||||
|
||||
typedef struct PoolBitmap {
|
||||
uint32_t *w;
|
||||
uint32_t nbits;
|
||||
uint32_t nwords;
|
||||
uint32_t cap;
|
||||
} PoolBitmap;
|
||||
|
||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx);
|
||||
int pool_bm_get_free_idx(PoolBitmap *bm);
|
||||
|
||||
#define POOL_DECLARE_TYPE(type) \
|
||||
typedef struct \
|
||||
{ \
|
||||
PoolBitmap bm; \
|
||||
type * objects; \
|
||||
} type##Pool; \
|
||||
type *pool_get_##type(); \
|
||||
void pool_free_##type(type *obj); \
|
||||
|
||||
#define POOL_DEFINE_TYPE(type, capacity) \
|
||||
static type type##_storage[capacity]; \
|
||||
static uint32_t type##_bitmap_w[POOL_BITMAP_BYTES] = {0}; \
|
||||
static type##Pool type##_pool = \
|
||||
{ \
|
||||
.bm = { \
|
||||
.w = type##_bitmap_w, \
|
||||
.nbits = POOL_BITS_PER_WORD, \
|
||||
.nwords = POOL_BITMAP_BYTES, \
|
||||
.cap = capacity, \
|
||||
}, \
|
||||
.objects = type##_storage, \
|
||||
}; \
|
||||
type * pool_get_##type() \
|
||||
{ \
|
||||
int free_offset = pool_bm_get_free_idx(&type##_pool.bm); \
|
||||
if(free_offset == -1) return NULL; \
|
||||
return &type##_pool.objects[free_offset]; \
|
||||
} \
|
||||
void pool_free_##type(type *entry) \
|
||||
{ \
|
||||
if(entry == NULL) return; \
|
||||
int offset = entry - &type##_pool.objects[0]; \
|
||||
pool_bm_clear_idx(&type##_pool.bm, offset); \
|
||||
}
|
||||
|
||||
#define POOL_GET(type) pool_get_##type()
|
||||
#define POOL_FREE(type, obj) pool_free_##type(obj)
|
||||
|
||||
#define POOL_ENTRY(name, capacity) \
|
||||
POOL_DECLARE_TYPE(name);
|
||||
#include POOLS_DEF_FILE
|
||||
#undef POOL_ENTRY
|
||||
|
||||
#endif // POOL_H
|
||||
+5
-1
@@ -5,12 +5,16 @@
|
||||
#include <maxmod.h>
|
||||
|
||||
#define CARD_SPRITE_SIZE 32
|
||||
#define MAX_SPRITES 128
|
||||
#define MAX_SPRITE_OBJECTS 16
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
OBJ_ATTR *obj;
|
||||
OBJ_AFFINE *aff;
|
||||
POINT pos;
|
||||
int idx;
|
||||
} Sprite;
|
||||
|
||||
// A sprite object is a sprite that is selectable and movable in animation
|
||||
@@ -63,4 +67,4 @@ Sprite* sprite_object_get_sprite(SpriteObject* sprite_object);
|
||||
void sprite_object_set_focus(SpriteObject* sprite_object, bool focus);
|
||||
bool sprite_object_is_focused(SpriteObject* sprite_object);
|
||||
|
||||
#endif // SPRITE_H
|
||||
#endif // SPRITE_H
|
||||
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ELF_FILE="${ELF_FILE-./build/balatro-gba.elf}"
|
||||
POOL_DEF_FILE="${POOL_DEF_FILE-./include/def_balatro_mempool.h}"
|
||||
READELF="${READELF-/opt/devkitpro/devkitARM/bin/arm-none-eabi-readelf}"
|
||||
TOTAL_BYTES=0
|
||||
|
||||
if [ ! -f "$POOL_DEF_FILE" ]; then
|
||||
echo "Mempool definition file not found: $POOL_DEF_FILE"
|
||||
echo "You can set your mempool definition file with:"
|
||||
echo " POOL_DEF_FILE=<mempool-def-file> $(basename $0)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$ELF_FILE" ]; then
|
||||
echo "elf file not found: $ELF_FILE"
|
||||
echo "You can set your elf file with:"
|
||||
echo " ELF_FILE=<elf-file> $(basename $0)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$READELF" ]; then
|
||||
echo "ERROR: \"$READELF\" is not an executable file."
|
||||
echo "You can override the file location for 'arm-none-eabi-readelf' with the READELF env variable."
|
||||
echo " e.g. $ READELF=\"/my/custom/location/arm-none-eabi-readelf\" $(basename $0) <file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_line_break() {
|
||||
echo "--------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
get_pool_names() {
|
||||
grep POOL_ENTRY "$POOL_DEF_FILE" | sed -n 's@.*(\(.*\)).*@\1@p' | sed 's@,@@g' | cut -d ' ' -f 1
|
||||
}
|
||||
|
||||
print_line_break
|
||||
printf "%-16s| %-10s | %-10s | %-10s | %-10s \n" "Object" "address" "pool size" "func size" "bitmap size"
|
||||
print_line_break
|
||||
|
||||
for name in $(get_pool_names); do
|
||||
output_pool="$( \
|
||||
"$READELF" -sW "$ELF_FILE" | \
|
||||
grep "${name}_" | \
|
||||
grep OBJECT | \
|
||||
grep storage | \
|
||||
sed -E 's@ +@ @g; s@^ @@' \
|
||||
)"
|
||||
output_func="$( \
|
||||
"$READELF" -sW "$ELF_FILE" | \
|
||||
grep -E "pool_free|pool_get|pool_init" | \
|
||||
grep -E "${name}$" | \
|
||||
sed -E 's@ +@ @g; s@^ @@' | \
|
||||
tr -d '\n' \
|
||||
)"
|
||||
output_bm="$( \
|
||||
"$READELF" -sW "$ELF_FILE" | \
|
||||
grep -E "${name}_bitmap_w" | \
|
||||
grep OBJECT | \
|
||||
sed -E 's@ +@ @g; s@^ @@' | \
|
||||
tr -d '\n' \
|
||||
)"
|
||||
|
||||
|
||||
address="$(cut -d ' ' -f 2 <<< $output_pool)"
|
||||
pool_size="$(cut -d ' ' -f 3 <<< $output_pool)"
|
||||
func_size="$(cut -d ' ' -f 3 <<< $output_func)"
|
||||
bm_size="$(cut -d ' ' -f 3 <<< $output_bm)"
|
||||
|
||||
TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bm_size ))
|
||||
|
||||
printf "%-16s| 0x%8s | %-10u | %-10u | %-10u \n" "$name" "$address" "$pool_size" "$func_size" "$bm_size"
|
||||
done
|
||||
|
||||
print_line_break
|
||||
echo Total bytes used: $TOTAL_BYTES
|
||||
+6
-6
@@ -9,6 +9,8 @@
|
||||
// Audio
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
// Card sprites lookup table. First index is the suit, second index is the rank. The value is the tile index.
|
||||
const static u16 card_sprite_lut[NUM_SUITS][NUM_RANKS] = {
|
||||
{0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192},
|
||||
@@ -25,7 +27,7 @@ void card_init()
|
||||
// Card methods
|
||||
Card *card_new(u8 suit, u8 rank)
|
||||
{
|
||||
Card *card = malloc(sizeof(Card));
|
||||
Card *card = POOL_GET(Card);
|
||||
|
||||
card->suit = suit;
|
||||
card->rank = rank;
|
||||
@@ -35,8 +37,7 @@ Card *card_new(u8 suit, u8 rank)
|
||||
|
||||
void card_destroy(Card **card)
|
||||
{
|
||||
if (*card == NULL) return;
|
||||
free(*card);
|
||||
POOL_FREE(Card, *card);
|
||||
*card = NULL;
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ u8 card_get_value(Card *card)
|
||||
// CardObject methods
|
||||
CardObject *card_object_new(Card *card)
|
||||
{
|
||||
CardObject *card_object = malloc(sizeof(CardObject));
|
||||
CardObject *card_object = POOL_GET(CardObject);
|
||||
|
||||
card_object->card = card;
|
||||
card_object->sprite_object = sprite_object_new();
|
||||
@@ -73,8 +74,7 @@ void card_object_destroy(CardObject **card_object)
|
||||
{
|
||||
if (*card_object == NULL) return;
|
||||
sprite_object_destroy(&((*card_object)->sprite_object));
|
||||
//card_destroy(&(*card_object)->card); // In practice, this is unnecessary because the card will be inserted into the discard pile and then back into the deck. If you need to destroy the card, you can do it manually before calling this function.
|
||||
free(*card_object);
|
||||
POOL_FREE(CardObject, *card_object);
|
||||
*card_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -11,10 +11,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
#define JOKER_SCORE_TEXT_Y 48
|
||||
#define NUM_JOKERS_PER_SPRITESHEET 2
|
||||
#define MAX_DEFINABLE_JOKERS 150
|
||||
|
||||
static const unsigned int *joker_gfxTiles[] =
|
||||
static const unsigned int *joker_gfxTiles[] =
|
||||
{
|
||||
#define DEF_JOKER_GFX(idx) joker_gfx##idx##Tiles,
|
||||
#include "../include/def_joker_gfx_table.h"
|
||||
@@ -48,7 +51,7 @@ static bool used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for j
|
||||
|
||||
// Maps the spritesheet index to the palette bank index allocated to it.
|
||||
// Spritesheets that were not allocated are
|
||||
static int* joker_spritesheet_pb_map;
|
||||
static int joker_spritesheet_pb_map[(MAX_DEFINABLE_JOKERS + 1) / NUM_JOKERS_PER_SPRITESHEET];
|
||||
static int joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = { 0 };
|
||||
|
||||
static int get_num_spritesheets()
|
||||
@@ -123,7 +126,6 @@ void joker_init()
|
||||
{
|
||||
// This should init once only so no need to free
|
||||
int num_spritesheets = get_num_spritesheets();
|
||||
joker_spritesheet_pb_map = (int*)malloc(sizeof(int) * num_spritesheets);
|
||||
|
||||
for (int i = 0; i < num_spritesheets; i++)
|
||||
{
|
||||
@@ -135,7 +137,7 @@ Joker *joker_new(u8 id)
|
||||
{
|
||||
if (id >= get_joker_registry_size()) return NULL;
|
||||
|
||||
Joker *joker = (Joker*)malloc(sizeof(Joker));
|
||||
Joker *joker = POOL_GET(Joker);
|
||||
const JokerInfo *jinfo = get_joker_registry_entry(id);
|
||||
|
||||
joker->id = id;
|
||||
@@ -149,8 +151,7 @@ Joker *joker_new(u8 id)
|
||||
|
||||
void joker_destroy(Joker **joker)
|
||||
{
|
||||
if (*joker == NULL) return;
|
||||
free(*joker);
|
||||
POOL_FREE(Joker, *joker);
|
||||
*joker = NULL;
|
||||
}
|
||||
|
||||
@@ -175,7 +176,7 @@ int joker_get_sell_value(const Joker* joker)
|
||||
// JokerObject methods
|
||||
JokerObject *joker_object_new(Joker *joker)
|
||||
{
|
||||
JokerObject *joker_object = malloc(sizeof(JokerObject));
|
||||
JokerObject *joker_object = POOL_GET(JokerObject);
|
||||
|
||||
int layer = 0;
|
||||
for (int i = 0; i < MAX_JOKER_OBJECTS; i++)
|
||||
@@ -232,7 +233,7 @@ void joker_object_destroy(JokerObject **joker_object)
|
||||
|
||||
sprite_object_destroy(&(*joker_object)->sprite_object); // Destroy the sprite
|
||||
joker_destroy(&(*joker_object)->joker); // Destroy the joker
|
||||
free(*joker_object);
|
||||
POOL_FREE(JokerObject, *joker_object);
|
||||
*joker_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "pool.h"
|
||||
#include "util.h"
|
||||
|
||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx)
|
||||
{
|
||||
uint32_t i = idx / POOL_BITS_PER_WORD;
|
||||
uint32_t b = idx % POOL_BITS_PER_WORD;
|
||||
|
||||
// Below are the "fast" forms of the above operations, respectively.
|
||||
// These are more efficient, but removed for readability
|
||||
// See: https://github.com/cellos51/balatro-gba/pull/132#discussion_r2365966071
|
||||
// Divide by 32 to get the word index
|
||||
//uint32_t i = idx >> 5;
|
||||
// Get last 5-bits, same as a modulo (% 32) operation on positive numbers
|
||||
//uint32_t b = idx & 0x1F;
|
||||
bm->w[i] &= ~((uint32_t)1 << b);
|
||||
}
|
||||
|
||||
int pool_bm_get_free_idx(PoolBitmap *bm)
|
||||
{
|
||||
for (uint32_t i = 0; i < bm->nwords; i++)
|
||||
{
|
||||
uint32_t inv = ~bm->w[i];
|
||||
|
||||
// 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
|
||||
//
|
||||
// By using the bitwise inverse of the word, you can skip words that are full
|
||||
// quickly (where the value is 0 or 'false' since all bits are '1', or 'in use'). Any value greater
|
||||
// than 0 indicates there is a free slot. Then, when counting the trailing 0's, you can test very quickly
|
||||
// where the first free slot is. This operation prevents looping through every bit of filled flags, and
|
||||
// will instead operate only on the first word with free slots.
|
||||
if (inv)
|
||||
{
|
||||
int bit = __builtin_ctz(inv);
|
||||
bm->w[i] |= ((uint32_t)1 << bit);
|
||||
int idx = i * POOL_BITS_PER_WORD + bit;
|
||||
return (idx < bm->cap) ? idx : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
#define POOL_ENTRY(name, capacity) \
|
||||
POOL_DEFINE_TYPE(name, capacity);
|
||||
#include POOLS_DEF_FILE
|
||||
#undef POOL_ENTRY
|
||||
+19
-11
@@ -3,6 +3,7 @@
|
||||
#include "util.h"
|
||||
#include "audio_utils.h"
|
||||
#include "soundbank.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include <tonc.h>
|
||||
#include <stdlib.h>
|
||||
@@ -21,7 +22,7 @@ static bool free_affines[MAX_AFFINES] = {false};
|
||||
// Sprite methods
|
||||
Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
{
|
||||
Sprite *sprite = malloc(sizeof(Sprite));
|
||||
Sprite* sprite = POOL_GET(Sprite);
|
||||
|
||||
sprite->obj = NULL;
|
||||
sprite->aff = NULL;
|
||||
@@ -32,7 +33,7 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
}
|
||||
else
|
||||
{
|
||||
free(sprite);
|
||||
POOL_FREE(Sprite, sprite);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
|
||||
if (aff_index == MAX_AFFINES)
|
||||
{
|
||||
free(sprite);
|
||||
POOL_FREE(Sprite, sprite);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -62,32 +63,39 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
sprite->aff = &obj_aff_buffer[aff_index];
|
||||
obj_set_attr(sprite->obj, a0, a1, ATTR2_PALBANK(pb) | tid);
|
||||
obj_aff_identity(&obj_aff_buffer[aff_index]);
|
||||
return sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->obj = &obj_buffer[sprite_index];
|
||||
obj_set_attr(sprite->obj, a0, a1, ATTR2_PALBANK(pb) | tid);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
sprite->idx = sprite_index;
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
void sprite_destroy(Sprite **sprite)
|
||||
{
|
||||
if (*sprite == NULL) return;
|
||||
|
||||
obj_hide((*sprite)->obj);
|
||||
free_sprites[(*sprite)->obj - obj_buffer] = NULL;
|
||||
|
||||
if ((*sprite)->aff != NULL)
|
||||
{
|
||||
free_affines[(*sprite)->aff - obj_aff_buffer] = false;
|
||||
}
|
||||
free(*sprite);
|
||||
|
||||
free_sprites[(*sprite)->idx] = NULL;
|
||||
|
||||
POOL_FREE(Sprite, *sprite);
|
||||
|
||||
*sprite = NULL;
|
||||
}
|
||||
|
||||
int sprite_get_layer(Sprite *sprite)
|
||||
{
|
||||
if (sprite == NULL || sprite->obj == NULL) return -1;
|
||||
if (sprite == NULL || sprite->obj == NULL) return UNDEFINED;
|
||||
return sprite->obj - obj_buffer;
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ int sprite_get_pb(const Sprite *sprite)
|
||||
// SpriteObject methods
|
||||
SpriteObject* sprite_object_new()
|
||||
{
|
||||
SpriteObject* sprite_object = (SpriteObject*)malloc(sizeof(SpriteObject));
|
||||
SpriteObject* sprite_object = POOL_GET(SpriteObject);
|
||||
sprite_object->sprite = NULL;
|
||||
sprite_object_reset_transform(sprite_object);
|
||||
sprite_object->selected = false;
|
||||
@@ -127,8 +135,8 @@ SpriteObject* sprite_object_new()
|
||||
void sprite_object_destroy(SpriteObject** sprite_object)
|
||||
{
|
||||
if (*sprite_object == NULL) return;
|
||||
sprite_destroy(&((*sprite_object)->sprite));
|
||||
free(*sprite_object);
|
||||
sprite_destroy(&(*sprite_object)->sprite);
|
||||
POOL_FREE(SpriteObject, *sprite_object);
|
||||
*sprite_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
POOLS_DEF_FILE := \"def_test_mempool.h\"
|
||||
|
||||
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,6 @@
|
||||
#include "test_structures.h"
|
||||
|
||||
#define TEST_SIZE 240
|
||||
|
||||
POOL_ENTRY(ChunkOfData, TEST_SIZE);
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "pool.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "test_structures.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct timespec timestamp_t;
|
||||
|
||||
timestamp_t get_time(void)
|
||||
{
|
||||
timestamp_t t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return t;
|
||||
}
|
||||
|
||||
void print_time_diff(timestamp_t start, timestamp_t end)
|
||||
{
|
||||
int64_t diff_nsec = end.tv_nsec - start.tv_nsec;
|
||||
|
||||
printf("Elapsed: %ld ns\n", diff_nsec);
|
||||
}
|
||||
|
||||
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 - 1); itr >= 0; --itr)
|
||||
{
|
||||
POOL_FREE(ChunkOfData, myPtrs[itr]);
|
||||
myPtrs[itr] = NULL;
|
||||
}
|
||||
|
||||
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]);
|
||||
myPtrs[itr] = NULL;
|
||||
}
|
||||
|
||||
if(!test_fill(myPtrs, number_to_remove)) return false;
|
||||
|
||||
for(int itr = 0; itr < TEST_SIZE; itr++)
|
||||
{
|
||||
POOL_FREE(ChunkOfData, myPtrs[itr]);
|
||||
myPtrs[itr] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Test it twice to make sure empty works, kinda hacky.
|
||||
printf("Testing Pool Fill and Empty 1x.\n");
|
||||
if(!test_fill_and_empty()) return UNDEFINED;
|
||||
printf("Testing Pool Fill and Empty 2x.\n");
|
||||
if(!test_fill_and_empty()) return UNDEFINED;
|
||||
|
||||
// Similarly here, verify that fill, random num removal, refill, and empty
|
||||
// by refilling and emptying again
|
||||
printf("Testing Pool Fill, Partial Empty, Refill, Empty.\n");
|
||||
if(!test_fill_and_remove_at_random_and_refill_and_empty()) return UNDEFINED;
|
||||
|
||||
printf("Testing Pool Fill and Empty.\n");
|
||||
if(!test_fill_and_empty()) return UNDEFINED;
|
||||
|
||||
printf("---------------------------------------------------------\n");
|
||||
printf("Pool Tests Passed\n");
|
||||
printf("---------------------------------------------------------\n");
|
||||
printf("Testing execution time for fun :)\n\n");
|
||||
|
||||
timestamp_t t1 = get_time();
|
||||
ChunkOfData* myData = POOL_GET(ChunkOfData);
|
||||
timestamp_t t2 = get_time();
|
||||
printf("Pool Get One:\n\t");
|
||||
print_time_diff(t1, t2);
|
||||
printf("\n");
|
||||
|
||||
t1 = get_time();
|
||||
POOL_FREE(ChunkOfData, myData);
|
||||
t2 = get_time();
|
||||
printf("Pool Free One:\n\t");
|
||||
print_time_diff(t1, t2);
|
||||
printf("\n");
|
||||
|
||||
t1 = get_time();
|
||||
myData = (ChunkOfData*)malloc(sizeof(ChunkOfData));
|
||||
t2 = get_time();
|
||||
printf("Malloc One:\n\t");
|
||||
print_time_diff(t1, t2);
|
||||
printf("\n");
|
||||
|
||||
t1 = get_time();
|
||||
free(myData);
|
||||
t2 = get_time();
|
||||
printf("Free One:\n\t");
|
||||
print_time_diff(t1, t2);
|
||||
printf("\n");
|
||||
|
||||
t1 = get_time();
|
||||
if(!test_fill_and_empty()) return UNDEFINED;
|
||||
t2 = get_time();
|
||||
printf("Fill and Empty Pool %d times:\n\t", TEST_SIZE);
|
||||
print_time_diff(t1, t2);
|
||||
printf("\n");
|
||||
|
||||
t1 = get_time();
|
||||
ChunkOfData* myPtrs[TEST_SIZE];
|
||||
for(int i = 0; i < TEST_SIZE; i++)
|
||||
{
|
||||
myPtrs[i] = (ChunkOfData*)malloc(sizeof(ChunkOfData));
|
||||
}
|
||||
for(int i = 0; i < TEST_SIZE; i++)
|
||||
{
|
||||
free(myPtrs[i]);
|
||||
}
|
||||
t2 = get_time();
|
||||
printf("Fill and Empty Malloc %d times:\n\t", TEST_SIZE);
|
||||
print_time_diff(t1, t2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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