[RELEASE] u22-v04

This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
2020-10-09 00:00:00 +00:00
parent 84c3a02b9a
commit 80b3b82332
277 changed files with 12154 additions and 13836 deletions

View File

@@ -1,8 +1,11 @@
add_compile_options(${LEFTONE_C_FLAGS})
add_subdirectory(chaos)
add_subdirectory(coly2d)
add_subdirectory(container)
add_subdirectory(conv)
add_subdirectory(dictres)
add_subdirectory(flasy)
add_subdirectory(gleasy)
add_subdirectory(glyphas)
add_subdirectory(jukebox)
@@ -10,3 +13,4 @@ add_subdirectory(math)
add_subdirectory(memory)
add_subdirectory(mpkutil)
add_subdirectory(parsarg)
add_subdirectory(statman)

18
util/flasy/CMakeLists.txt Normal file
View File

@@ -0,0 +1,18 @@
add_library(flasy
flasy.c
)
target_link_libraries(flasy
memory
tinycthread
)
if (BUILD_TESTING)
add_executable(flasy-test test.c)
target_link_libraries(flasy-test flasy)
set(workdir "${CMAKE_CURRENT_BINARY_DIR}/test")
file(MAKE_DIRECTORY ${workdir})
add_test(
NAME flasy-test
COMMAND bash -c "../flasy-test $(seq 9)"
WORKING_DIRECTORY ${workdir})
endif()

4
util/flasy/README.md Normal file
View File

@@ -0,0 +1,4 @@
filasy
====
ASYnc file FLusher

146
util/flasy/flasy.c Normal file
View File

@@ -0,0 +1,146 @@
#include "./flasy.h"
#include <assert.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <tinycthread.h>
#include "util/memory/memory.h"
typedef struct {
atomic_uintptr_t fp;
atomic_bool request_flush;
uint8_t buf[1];
} flasy_handler_t;
struct flasy_t {
mtx_t mtx;
thrd_t thread;
size_t bufsz;
atomic_bool alive;
size_t length;
uint8_t handlers[1];
};
static int flasy_main_(void* srv_) {
assert(srv_ != NULL);
flasy_t* srv = srv_;
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
uint8_t* end = srv->handlers + hsize*srv->length;
for (;;) {
const bool alive = atomic_load(&srv->alive);
for (uint8_t* itr = srv->handlers; itr < end; itr += hsize) {
flasy_handler_t* handler = (typeof(handler)) itr;
FILE* fp = (typeof(fp)) atomic_load(&handler->fp);
if (fp != NULL && atomic_load(&handler->request_flush)) {
/* while request_flush is true and fp isn't NULL,
no other thread modifies the handler */
fclose(fp);
atomic_store(&handler->fp, 0);
}
}
thrd_sleep(&(struct timespec) {
.tv_sec = 0,
.tv_nsec = 10000000, /* = 10 ms */
}, NULL);
if (!alive) break;
}
return EXIT_SUCCESS;
}
flasy_t* flasy_new(size_t bufsz, size_t hlen) {
assert(bufsz > 0);
assert(hlen > 0);
const size_t hsize = bufsz + offsetof(flasy_handler_t, buf);
flasy_t* srv = memory_new(sizeof(*srv) + hlen*hsize - 1);
*srv = (typeof(*srv)) {
.bufsz = bufsz,
.length = hlen,
};
atomic_store(&srv->alive, true);
uint8_t* itr = srv->handlers;
for (size_t i = 0; i < srv->length; ++i) {
atomic_init(&((flasy_handler_t*) itr)->fp, 0);
itr += hsize;
}
if (mtx_init(&srv->mtx, mtx_plain) != thrd_success) {
fprintf(stderr, "flasy: failed to create mutex\n");
abort();
}
if (thrd_create(&srv->thread, &flasy_main_, srv) != thrd_success) {
fprintf(stderr, "flasy: failed to create thread\n");
abort();
}
return srv;
}
void flasy_delete(flasy_t* srv) {
if (srv == NULL) return;
atomic_store(&srv->alive, false);
thrd_join(srv->thread, NULL);
mtx_destroy(&srv->mtx);
memory_delete(srv);
}
FILE* flasy_open_file(flasy_t* srv, const char* path, bool binary) {
assert(srv != NULL);
assert(path != NULL);
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
uint8_t* itr = srv->handlers;
uint8_t* end = srv->handlers + hsize*srv->length;
for (; itr < end; itr += hsize) {
if (atomic_load(&((flasy_handler_t*) itr)->fp) == 0) break;
}
if (itr >= end) return NULL;
FILE* fp = fopen(path, binary? "wb": "w");
if (fp == NULL) return NULL;
flasy_handler_t* handler = (typeof(handler)) itr;
/* while handler->fp is NULL, no other threads modifies the handler */
setvbuf(fp, (char*) handler->buf, _IOFBF, srv->bufsz);
atomic_store(&handler->request_flush, false);
atomic_store(&handler->fp, (uintptr_t) fp);
return fp;
}
void flasy_close_file(flasy_t* srv, FILE* fp) {
assert(srv != NULL);
assert(fp != NULL);
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
uint8_t* itr = srv->handlers;
uint8_t* end = srv->handlers + hsize*srv->length;
for (; itr < end; itr += hsize) {
if (atomic_load(&((flasy_handler_t*) itr)->fp) == (uintptr_t) fp) break;
}
assert(itr < end);
flasy_handler_t* handler = (typeof(handler)) itr;
atomic_store(&handler->request_flush, true);
}

34
util/flasy/flasy.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
/* plz note that all functions are not thread-safe */
struct flasy_t;
typedef struct flasy_t flasy_t;
flasy_t* /* OWNERSHIP */
flasy_new(
size_t bufsz,
size_t hlen
);
void
flasy_delete(
flasy_t* srv /* OWNERSHIP */
);
FILE* /* OWNERSHIP/NULLABLE */
flasy_open_file(
flasy_t* srv,
const char* path,
bool binary
);
void
flasy_close_file(
flasy_t* srv,
FILE* fp /* OWNERSHIP */
);

46
util/flasy/test.c Normal file
View File

@@ -0,0 +1,46 @@
#undef NDEBUG
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./flasy.h"
#define MAX_ 16
#define COUNT_ (512*1024)
int main(int argc, char** argv) {
assert(2 <= argc && argc <= MAX_);
flasy_t* srv = flasy_new(COUNT_*sizeof(uint32_t), MAX_);
for (size_t i = 0; i < (size_t) argc-1; ++i) {
FILE* fp = flasy_open_file(srv, argv[1+i], true);
assert(fp != NULL);
for (uint32_t i = 0; i < COUNT_; ++i) {
assert(fwrite(&i, sizeof(i), 1, fp) == 1);
}
flasy_close_file(srv, fp);
}
flasy_delete(srv); /* join */
for (size_t i = 0; i < (size_t) argc-1; ++i) {
FILE* fp = fopen(argv[1+i], "rb");
assert(fp != NULL);
for (uint32_t i = 0; i < COUNT_; ++i) {
uint32_t a;
assert(fread(&a, sizeof(a), 1, fp) == 1);
if (a != i) printf("%d %d\n", i, a);
assert(a == i);
}
uint8_t dummy_;
assert(fread(&dummy_, 1, 1, fp) == 0 && feof(fp) != 0);
fclose(fp);
}
return EXIT_SUCCESS;
}

View File

@@ -50,7 +50,8 @@ static void gleasy_atlas_resize_bitmap_(
const size_t pixel = type*fmt;
assert(pixel > 0);
container_array_resize(atlas->resize_buffer, out->width*out->height*pixel);
container_array_resize(
atlas->resize_buffer, (size_t) out->width*out->height*pixel);
out->buffer = atlas->resize_buffer;
const int32_t ymax = out->height * pixel;
@@ -128,7 +129,7 @@ gleasy_atlas_t* gleasy_atlas_new(
assert(glGetError() == GL_NO_ERROR);
container_array_reserve(atlas->resize_buffer, width*height*4);
container_array_reserve(atlas->resize_buffer, (size_t) width*height*4);
return atlas;
}

View File

@@ -27,7 +27,7 @@ target_link_libraries(glyphas
if (BUILD_TESTING)
add_executable(glyphas-test test.c)
target_link_libraries(glyphas-test
SDL2::SDL2
${SDL2_TARGET}
glyphas)
endif()

View File

@@ -9,31 +9,20 @@
#include "util/gleasy/texture.h"
#include "util/gleasy/program.h"
#include "util/math/vector.h"
#include "util/memory/memory.h"
#include "./block.h"
/* resources */
#include "anysrc/drawer.vshader.h"
#include "anysrc/drawer.fshader.h"
#include "util/glyphas/anysrc/drawer.vshader.h"
#include "util/glyphas/anysrc/drawer.fshader.h"
#define GLYPHAS_DRAWER_UNIFORM_TEX 0
#define UNIFORM_TEX_ 0
#define GLYPHAS_DRAWER_VSHADER_IN_POS 0
#define GLYPHAS_DRAWER_VSHADER_IN_SIZE 1
#define GLYPHAS_DRAWER_VSHADER_IN_UV_POS 2
#define GLYPHAS_DRAWER_VSHADER_IN_UV_SIZE 3
#define GLYPHAS_DRAWER_VSHADER_IN_COLOR 4
struct glyphas_drawer_t {
gleasy_texture_2d_t tex;
GLuint vao;
gleasy_buffer_array_t instances;
size_t instances_reserved;
size_t instances_length;
};
#define VSHADER_IN_POS_ 0
#define VSHADER_IN_SIZE_ 1
#define VSHADER_IN_UV_POS_ 2
#define VSHADER_IN_UV_SIZE_ 3
#define VSHADER_IN_COLOR_ 4
#pragma pack(push, 1)
typedef struct {
@@ -45,73 +34,60 @@ typedef struct {
} glyphas_drawer_instance_t;
#pragma pack(pop)
static void glyphas_drawer_setup_vao_(
gleasy_buffer_array_t instances) {
assert(instances != 0);
glBindBuffer(GL_ARRAY_BUFFER, instances);
# define enable_attrib_(NAME, name, dim, type) do { \
glEnableVertexAttribArray(GLYPHAS_DRAWER_VSHADER_IN_##NAME); \
glVertexAttribPointer( \
GLYPHAS_DRAWER_VSHADER_IN_##NAME, dim, type, GL_FALSE, \
sizeof(glyphas_drawer_instance_t), \
NULL + offsetof(glyphas_drawer_instance_t, name)); \
glVertexAttribDivisor(GLYPHAS_DRAWER_VSHADER_IN_##NAME, 1); \
} while (0)
enable_attrib_(POS, pos, 2, GL_FLOAT);
enable_attrib_(SIZE, size, 2, GL_FLOAT);
enable_attrib_(UV_POS, uv_pos, 2, GL_FLOAT);
enable_attrib_(UV_SIZE, uv_size, 2, GL_FLOAT);
enable_attrib_(COLOR, color, 4, GL_FLOAT);
# undef enable_attrib_
}
gleasy_program_t glyphas_drawer_create_default_program(void) {
return gleasy_program_new("", 0,
glyphas_drawer_vshader_, sizeof(glyphas_drawer_vshader_),
glyphas_drawer_fshader_, sizeof(glyphas_drawer_fshader_));
}
glyphas_drawer_t* glyphas_drawer_new(void) {
glyphas_drawer_t* drawer = memory_new(sizeof(*drawer));
*drawer = (typeof(*drawer)) {0};
void glyphas_drawer_initialize(
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex) {
assert(drawer != NULL);
*drawer = (typeof(*drawer)) {
.tex = tex,
};
glCreateVertexArrays(1, &drawer->vao);
glBindVertexArray(drawer->vao);
glGenBuffers(1, &drawer->instances);
glyphas_drawer_setup_vao_(drawer->instances);
return drawer;
glBindVertexArray(drawer->vao);
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
# define enable_(index, var, dim, type) do { \
glEnableVertexAttribArray(index); \
glVertexAttribPointer( \
index, dim, type, GL_FALSE, \
sizeof(glyphas_drawer_instance_t), \
NULL + offsetof(glyphas_drawer_instance_t, var)); \
glVertexAttribDivisor(index, 1); \
} while (0)
enable_(VSHADER_IN_POS_, pos, 2, GL_FLOAT);
enable_(VSHADER_IN_SIZE_, size, 2, GL_FLOAT);
enable_(VSHADER_IN_UV_POS_, uv_pos, 2, GL_FLOAT);
enable_(VSHADER_IN_UV_SIZE_, uv_size, 2, GL_FLOAT);
enable_(VSHADER_IN_COLOR_, color, 4, GL_FLOAT);
# undef enable_attrib_
}
void glyphas_drawer_delete(glyphas_drawer_t* drawer) {
if (drawer == NULL) return;
void glyphas_drawer_deinitialize(glyphas_drawer_t* drawer) {
assert(drawer != NULL);
glDeleteBuffers(1, &drawer->instances);
glDeleteVertexArrays(1, &drawer->vao);
memory_delete(drawer);
}
void glyphas_drawer_clear(
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex, size_t reserve) {
void glyphas_drawer_clear(glyphas_drawer_t* drawer, size_t reserve) {
assert(drawer != NULL);
assert(tex != 0);
assert(reserve > 0);
drawer->tex = tex;
drawer->instances_length = 0;
if (drawer->instances_reserved < reserve) {
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
glBufferData(GL_ARRAY_BUFFER,
reserve * sizeof(glyphas_drawer_instance_t),
NULL, GL_DYNAMIC_DRAW);
reserve*sizeof(glyphas_drawer_instance_t), NULL, GL_DYNAMIC_DRAW);
drawer->instances_reserved = reserve;
}
}
@@ -158,7 +134,7 @@ void glyphas_drawer_draw(const glyphas_drawer_t* drawer) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, drawer->tex);
glUniform1i(GLYPHAS_DRAWER_UNIFORM_TEX, 0);
glUniform1i(UNIFORM_TEX_, 0);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, drawer->instances_length);
}

View File

@@ -1,33 +1,41 @@
#pragma once
#include "util/gleasy/buffer.h"
#include "util/gleasy/texture.h"
#include "util/gleasy/program.h"
#include "./block.h"
struct glyphas_drawer_t;
typedef struct glyphas_drawer_t glyphas_drawer_t;
typedef struct {
gleasy_texture_2d_t tex;
GLuint vao;
gleasy_buffer_array_t instances;
size_t instances_reserved;
size_t instances_length;
} glyphas_drawer_t;
gleasy_program_t
glyphas_drawer_create_default_program(
void
);
glyphas_drawer_t*
glyphas_drawer_new(
void
void
glyphas_drawer_initialize(
glyphas_drawer_t* drawer,
gleasy_texture_2d_t tex
);
void
glyphas_drawer_delete(
glyphas_drawer_deinitialize(
glyphas_drawer_t* drawer
);
void
glyphas_drawer_clear(
glyphas_drawer_t* drawer,
gleasy_texture_2d_t tex,
size_t reserve
glyphas_drawer_t* drawer,
size_t reserve
);
void
@@ -44,7 +52,7 @@ glyphas_drawer_add_block_item(
/* The drawer doesn't hold the reference. */
);
/* bind the program before drawing */
/* call glUseProgram before calling */
void
glyphas_drawer_draw(
const glyphas_drawer_t* drawer

View File

@@ -11,6 +11,16 @@
#include "./context.h"
static const char* glyphas_face_get_ft_error_str_(FT_Error err) {
/* what a fucking trick lol.
* https://stackoverflow.com/questions/61641364/gcc-cant-find-ft-error-string-when-trying-to-compile */
# undef FTERRORS_H_
# define FT_ERRORDEF(code, val, str) case code: return str;
# define FT_ERROR_START_LIST switch(err) {
# define FT_ERROR_END_LIST default: return "unknown"; }
# include FT_ERRORS_H
}
void glyphas_face_initialize_from_file(
glyphas_face_t* face,
const glyphas_context_t* ctx,
@@ -23,8 +33,8 @@ void glyphas_face_initialize_from_file(
const FT_Error err = FT_New_Face(ctx->ft, path, index, &face->ft);
if (err != FT_Err_Ok) {
fprintf(stderr,
"failed to load font file '%s': %s\n", path, FT_Error_String(err));
fprintf(stderr, "failed to load font file '%s': %s\n",
path, glyphas_face_get_ft_error_str_(err));
abort();
}
}
@@ -43,8 +53,8 @@ void glyphas_face_initialize_from_buffer(
const FT_Error err =
FT_New_Memory_Face(ctx->ft, data, length, index, &face->ft);
if (err != FT_Err_Ok) {
fprintf(stderr,
"failed to load font on memory: %s\n", FT_Error_String(err));
fprintf(stderr, "failed to load font on memory: %s\n",
glyphas_face_get_ft_error_str_(err));
abort();
}
}

View File

@@ -28,8 +28,8 @@ typedef struct {
gleasy_atlas_t* atlas;
glyphas_cache_t* cache;
gleasy_program_t prog;
glyphas_drawer_t* drawer;
gleasy_program_t prog;
glyphas_drawer_t drawer;
} context_t;
#define SCALE 0.01f
@@ -56,8 +56,8 @@ static void align_text_(context_t* ctx, const char* str) {
static const vec2_t origin = vec2(.5f, -.5f);
glyphas_block_set_origin(block, &origin);
glyphas_drawer_clear(ctx->drawer, gleasy_atlas_get_texture(ctx->atlas), len);
glyphas_drawer_add_block(ctx->drawer, block);
glyphas_drawer_clear(&ctx->drawer, len);
glyphas_drawer_add_block(&ctx->drawer, block);
glyphas_block_delete(block);
}
@@ -72,8 +72,8 @@ static void initialize_(context_t* ctx, const char* path, const char* str) {
ctx->atlas = gleasy_atlas_new(GL_RED, 256, 256, false /* anti-alias */);
ctx->cache = glyphas_cache_new(ctx->atlas, &ctx->face, 16, 16);
ctx->prog = glyphas_drawer_create_default_program();
ctx->drawer = glyphas_drawer_new();
ctx->prog = glyphas_drawer_create_default_program();
glyphas_drawer_initialize(&ctx->drawer, gleasy_atlas_get_texture(ctx->atlas));
assert(glGetError() == GL_NO_ERROR);
align_text_(ctx, str);
@@ -87,13 +87,13 @@ static void draw_(const context_t* ctx) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(ctx->prog);
glyphas_drawer_draw(ctx->drawer);
glyphas_drawer_draw(&ctx->drawer);
}
static void deinitialize_(context_t* ctx) {
assert(ctx != NULL);
glyphas_drawer_delete(ctx->drawer);
glyphas_drawer_deinitialize(&ctx->drawer);
glDeleteProgram(ctx->prog);
glyphas_cache_delete(ctx->cache);

View File

@@ -49,7 +49,7 @@ static void jukebox_composite_affect_(
jukebox_effect_affect(c->effects[i], &chunk_pcm);
}
const size_t len = chunk_pcm.frames * channels;
const size_t len = (size_t) chunk_pcm.frames*channels;
const float* src = chunk;
float* dst = pcm->ptr + read * channels;
for (uint64_t i = 0; i < len; ++i) {

View File

@@ -8,7 +8,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "thirdparty/miniaudio/miniaudio.h"
#include <miniaudio.h>
#include "util/math/algorithm.h"
#include "util/math/rational.h"

View File

@@ -32,7 +32,7 @@ static void jukebox_delay_affect_(
jukebox_delay_t* d = (typeof(d)) effect;
const int32_t ch = d->format.channels;
const size_t pcmlen = pcm->frames*ch;
const size_t pcmlen = (size_t) pcm->frames*ch;
for (size_t i = 0; i < pcmlen; ++i) {
float* bufoff = &d->buffer[d->offset];

View File

@@ -5,7 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "thirdparty/miniaudio/miniaudio.h"
#include <miniaudio.h>
#include "util/container/array.h"
#include "util/memory/memory.h"

View File

@@ -6,7 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "thirdparty/miniaudio/miniaudio.h"
#include <miniaudio.h>
#include "util/math/rational.h"
#include "util/memory/memory.h"

View File

@@ -14,7 +14,8 @@
#define MATH_CLAMP(x, min, max) ((x) < (min)? (min): (x) > (max)? (max): (x))
#define MATH_SIGN(a) (a < 0? -1: a > 0? 1: 0)
#define MATH_SIGN(a) (a < 0? -1: a > 0? 1: 0)
#define MATH_SIGN_NOZERO(a) (a < 0? -1: 1)
#define MATH_FLOAT_EQUAL(a, b) (MATH_ABS((a) - (b)) < MATH_EPSILON)
#define MATH_FLOAT_VALID(a) (!isnan(a) && !isinf(a))

View File

@@ -0,0 +1,9 @@
add_library(statman
statman.c
)
if (BUILD_TESTING)
add_executable(statman-test test.c)
target_link_libraries(statman-test statman)
add_test(statman-test statman-test)
endif()

4
util/statman/README.md Normal file
View File

@@ -0,0 +1,4 @@
statman
====
the simplest STATe MANagement system

89
util/statman/statman.c Normal file
View File

@@ -0,0 +1,89 @@
#include "./statman.h"
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define REDIRECT_MAX_ 100
static const statman_meta_t* statman_get_meta_by_state_(
const statman_meta_t* meta, statman_state_t state) {
assert(meta != NULL);
for (size_t i = 0; meta[i].name != NULL; ++i) {
if (meta[i].state == state) return &meta[i];
}
fprintf(stderr, "statman: unknown state %"PRIu16"\n", state);
abort();
}
static void statman_initialize_state_(
const statman_meta_t* meta,
void* instance,
statman_state_t* state,
size_t depth) {
assert(meta != NULL);
assert(state != NULL);
assert(depth < REDIRECT_MAX_);
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
if (m->initialize == NULL) return;
const statman_state_t backup = *state;
m->initialize(m, instance, state);
if (backup != *state) {
statman_initialize_state_(meta, instance, state, depth+1);
}
}
static void statman_update_state_(
const statman_meta_t* meta,
void* instance,
statman_state_t* state,
size_t depth) {
assert(meta != NULL);
assert(state != NULL);
assert(depth < REDIRECT_MAX_);
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
if (m->update == NULL) return;
const statman_state_t backup = *state;
m->update(m, instance, state);
if (backup != *state) {
statman_initialize_state_(meta, instance, state, depth+1);
statman_update_state_(meta, instance, state, depth+1);
}
}
void statman_update(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(state != NULL);
statman_update_state_(meta, instance, state, 0);
}
void statman_transition_to(
const statman_meta_t* meta,
void* instance,
statman_state_t* state,
statman_state_t next) {
assert(meta != NULL);
assert(state != NULL);
if (*state == next) return;
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
if (m->finalize != NULL) {
m->finalize(m, instance, &next);
}
if (*state == next) return;
*state = next;
statman_initialize_state_(meta, instance, state, 0);
}

47
util/statman/statman.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdint.h>
typedef uint16_t statman_state_t;
typedef struct statman_meta_t statman_meta_t;
struct statman_meta_t {
statman_state_t state;
const char* name;
const void* data;
void
(*initialize)(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
(*update)(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
(*finalize)(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
};
void
statman_update(
const statman_meta_t* meta,
void* instance,
statman_state_t* state
);
/* don't call in callback functions */
void
statman_transition_to(
const statman_meta_t* meta,
void* instance,
statman_state_t* state,
statman_state_t next
);

160
util/statman/test.c Normal file
View File

@@ -0,0 +1,160 @@
#undef NDEBUG
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "./statman.h"
typedef enum {
CHECK_POINT_NONE,
CHECK_POINT_STATE0_INITIALIZE,
CHECK_POINT_STATE1_INITIALIZE,
CHECK_POINT_STATE2_INITIALIZE,
CHECK_POINT_STATE3_INITIALIZE,
CHECK_POINT_STATE0_UPDATE,
CHECK_POINT_STATE1_UPDATE,
CHECK_POINT_STATE2_UPDATE,
CHECK_POINT_STATE0_FINALIZE,
CHECK_POINT_STATE1_FINALIZE,
CHECK_POINT_STATE2_FINALIZE,
CHECK_POINT_STATE3_FINALIZE,
} check_point_t;
static check_point_t actual_[256] = {0};
static size_t actual_length_ = 0;
static inline void check_point_(check_point_t cp) {
assert(actual_length_ < sizeof(actual_)/sizeof(actual_[0]));
actual_[actual_length_++] = cp;
}
static void state_initialize_(
const statman_meta_t* meta, void* time, statman_state_t* next) {
assert(meta != NULL);
assert(time != NULL);
assert(next != NULL);
check_point_(CHECK_POINT_STATE0_INITIALIZE+meta->state);
}
static void state3_initialize_(
const statman_meta_t* meta, void* time, statman_state_t* next) {
assert(meta != NULL);
assert(time != NULL);
assert(next != NULL);
check_point_(CHECK_POINT_STATE3_INITIALIZE);
*next = 0;
}
static void state_update_(
const statman_meta_t* meta, void* time, statman_state_t* next) {
assert(meta != NULL);
assert(time != NULL);
assert(next != NULL);
check_point_(CHECK_POINT_STATE0_UPDATE+meta->state);
size_t *t = (typeof(t)) time;
if ((*t)++%2 == 0) *next = meta->state+1;
}
static void state_finalize_(
const statman_meta_t* meta, void* time, statman_state_t* next) {
assert(meta != NULL);
assert(time != NULL);
assert(next != NULL);
check_point_(CHECK_POINT_STATE0_FINALIZE+meta->state);
*next = 0;
}
static const statman_meta_t table_[] = {
{
.state = 0,
.name = "0",
.initialize = state_initialize_,
.update = state_update_,
.finalize = state_finalize_,
},
{
.state = 1,
.name = "1",
.initialize = state_initialize_,
.update = state_update_,
.finalize = state_finalize_,
},
{
.state = 2,
.name = "2",
.initialize = state_initialize_,
.update = state_update_,
.finalize = state_finalize_,
},
{
.state = 3,
.name = "3",
.initialize = state3_initialize_,
},
};
int main(void) {
statman_state_t state = 0;
size_t time = 0;
/* state0 -> state1 */
statman_update(table_, &time, &state);
assert(state == 1);
assert(time == 2);
/* state1 -> state2 */
statman_update(table_, &time, &state);
assert(state == 2);
assert(time == 4);
/* state2 -> state0 */
statman_update(table_, &time, &state);
assert(state == 0);
assert(time == 6);
/* state0 -> state1 */
statman_update(table_, &time, &state);
assert(state == 1);
assert(time == 8);
/* stat1's finalizer forces to move to 0 */
statman_transition_to(table_, &time, &state, 2);
assert(state == 0);
assert(time == 8);
static const check_point_t expects[] = {
CHECK_POINT_STATE0_UPDATE,
CHECK_POINT_STATE1_INITIALIZE,
CHECK_POINT_STATE1_UPDATE,
CHECK_POINT_STATE1_UPDATE,
CHECK_POINT_STATE2_INITIALIZE,
CHECK_POINT_STATE2_UPDATE,
CHECK_POINT_STATE2_UPDATE,
CHECK_POINT_STATE3_INITIALIZE,
CHECK_POINT_STATE0_INITIALIZE,
CHECK_POINT_STATE0_UPDATE,
CHECK_POINT_STATE0_UPDATE,
CHECK_POINT_STATE1_INITIALIZE,
CHECK_POINT_STATE1_UPDATE,
CHECK_POINT_STATE1_FINALIZE,
CHECK_POINT_STATE0_INITIALIZE,
};
assert(actual_length_ == sizeof(expects)/sizeof(expects[0]));
for (size_t i = 0; i < actual_length_; ++i) {
assert(expects[i] == actual_[i]);
}
}