From 69b419b08610a26d01c798b88b66188a3dcfbee6 Mon Sep 17 00:00:00 2001 From: mr4th Date: Mon, 6 Jul 2026 13:58:12 -0700 Subject: [PATCH] new example 'hooks' shows arena_push_symbol_set and a way to organize a hookable system --- build_with_cl.bat | 4 + build_with_clang.bat | 3 + build_with_clang.sh | 4 + examples/hooks.c | 214 +++++++++++++++++++++++++++++++++++++++ examples/hooks.h | 163 +++++++++++++++++++++++++++++ examples/stack_machine.c | 2 +- project.4coder | 7 +- 7 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 examples/hooks.c create mode 100644 examples/hooks.h diff --git a/build_with_cl.bat b/build_with_cl.bat index e025003..8c87406 100644 --- a/build_with_cl.bat +++ b/build_with_cl.bat @@ -13,4 +13,8 @@ if "%1" == "" ( if "%1" == "stack_machine" ( cl -Festack_machine %code%\examples\stack_machine.c %opts% %inc% %linkopts% ) +if "%1" == "hooks" ( + cl -Fehooks %code%\examples\hooks.c %opts% %inc% %linkopts% +) + popd diff --git a/build_with_clang.bat b/build_with_clang.bat index 9e9a531..3eb5455 100644 --- a/build_with_clang.bat +++ b/build_with_clang.bat @@ -14,6 +14,9 @@ if "%1" == "" ( if "%1" == "stack_machine" ( clang -o stack_machine %code%\examples\stack_machine.c %opts% %inc% %linkopts% ) +if "%1" == "hooks" ( + clang -o hooks %code%\examples\hooks.c %opts% %inc% %linkopts% +) popd diff --git a/build_with_clang.sh b/build_with_clang.sh index 77537e6..83846a2 100755 --- a/build_with_clang.sh +++ b/build_with_clang.sh @@ -15,4 +15,8 @@ elif [[ "$1" == "stack_machine" ]]; then clang -c $code/examples/stack_machine.c $opts $inc ./symbol_set.ld_meta stack_machine.o clang -o stack_machine $code/examples/stack_machine.c $opts $inc -T sy.ld +elif [[ "$1" == "hooks" ]]; then + clang -c $code/examples/hooks.c $opts $inc + ./symbol_set.ld_meta hooks.o + clang -o hooks $code/examples/hooks.c $opts $inc -T sy.ld fi diff --git a/examples/hooks.c b/examples/hooks.c new file mode 100644 index 0000000..2ffded9 --- /dev/null +++ b/examples/hooks.c @@ -0,0 +1,214 @@ +/* +** C Scripting - Hooks +*/ + +#define SY__MAIN 1 +#include "symbol_set.h" +#include "symbol_set.init.h" + +#include "hooks.h" +#include +#include +#include + +//////////////////////////////// +// Little Base Layer + +static Arena +arena_alloc(U64 cap){ + Arena arena = {0}; + arena.memory = malloc(cap); + arena.cap = cap; + return(arena); +} + +static void* +arena_push(Arena *arena, U64 size){ + void *result = 0; + if (arena->pos + size <= arena->cap){ + result = (U8*)arena->memory + arena->pos; + arena->pos += size; + arena->pos = (arena->pos + 7)&~7; // "round up to multiple of 8" + memset(result, 0, size); + } + return(result); +} + +static void +arena_pop_to(Arena *arena, U64 pos){ + if (pos < arena->pos){ + arena->pos = (pos + 7)&~7; // "round up to multiple of 8" + } +} + +static U64 +arena_pos(Arena *arena){ + return(arena->pos); +} + +static void* +copy_strided(void *dst, U32 dst_stride, + void *src, U32 src_stride, + U32 count){ + void *base_src = src; + void *base_dst = dst; + if (dst_stride != src_stride){ + U32 size = Min(dst_stride, src_stride); + for (U32 i = 0; i < count; i += 1){ + memcpy(dst, src, size); + dst = (U8*)dst + dst_stride; + src = (U8*)src + src_stride; + } + } + else{ + memcpy(dst, src, dst_stride*count); + } + return(base_dst); +} + +static void* +arena_push_symbol_set__raw(Arena *arena, + U32 size, void *src, U32 src_stride, U32 count){ + void *dst = push_array(arena, U8, size*(count + 1)); + copy_strided((U8*)dst + size, size, src, src_stride, count); + return(dst); +} + +//////////////////////////////// +// Pretend "Core API" + +typedef struct BufferRenderCtx{ + U32 dummy; +} BufferRenderCtx; + +static void +color_range(Ctx *ctx, U32 min, U32 max){ + printf("DEMO: color_word(%p, %u, %u)\n", ctx, min, max); +} + +//////////////////////////////// +// Hooks + +//// Symbols //// + +HOOK_POINT_TYPE_DEF(BeginFrame, void); +HOOK_POINT_TYPE_DEF(EndFrame, void); + +HOOK_POINT_STRUCT(RenderBuffer){ + U32 buffer_id; +}; + +PROCESS_TYPE(SearchWord); + +PROCESS_HOOK(SearchWord, RenderBuffer){ + // imagine you get the string from the process + String8 string = str8_const("c-scripting"); + + // imagine you get the match ranges from searching params->buffer_id + U32 range_count = 0; + U32 rmin[3] = {0}; + if (params->buffer_id == 1){ + range_count = 3; + memcpy(rmin, (U32[3]){ 1, 20, 152 }, 12); + } + else if (params->buffer_id == 2){ + range_count = 2; + memcpy(rmin, (U32[2]){ 72, 510 }, 8); + } + + // color the ranges + for (U32 i = 0; i < range_count; i += 1){ + color_range(ctx, rmin[i], rmin[i] + string.size); + } +} + +//// Rules //// + +static Rules +rules_from_symbol_sets(Arena *arena){ + // copy named symbol sets + U32 hook_point_count = SyCount(HOOK_POINT) + 1; + HookPoint *hook_points = arena_push_symbol_set(arena, HOOK_POINT); + + U32 process_type_count = SyCount(PROCESS_TYPE) + 1; + ProcessType *process_types = arena_push_symbol_set(arena, PROCESS_TYPE); + + // process unnamed symbol sets + HookFunc **func__process_hook = + push_array(arena, HookFunc*, hook_point_count*process_type_count); + for (SyEach(PROCESS_HOOK, link)){ + U32 idx = link->process_type_id*hook_point_count + link->hook_point_id; + func__process_hook[idx] = link->func; + } + + // rules + Rules rules = { + .hook_point_count = hook_point_count, + .process_type_count = process_type_count, + .hook_points = hook_points, + .process_types = process_types, + .func__process_hook = func__process_hook + }; + return(rules); +} + +static HookFunc* +rules_func_from_process_hook(Rules *rules, U32 process_type_id, U32 hook_point_id){ + U32 idx = process_type_id*rules->hook_point_count + hook_point_id; + HookFunc *result = rules->func__process_hook[idx]; + return(result); +} + +//// Context Functions //// + +static void +ctx_new_process(Ctx *ctx, U32 process_type_id){ + if (ctx->process_count < sizeof(ctx->process_type_id)/sizeof(*ctx->process_type_id)){ + ctx->process_type_id[ctx->process_count] = process_type_id; + ctx->process_count += 1; + } +} + +static void +ctx_hook_all_processes(Ctx *ctx, U32 hookp_id, void *params){ + for (U32 i = 0; i < ctx->process_count; i += 1){ + U32 process_type_id = ctx->process_type_id[i]; + HookFunc *func = rules_func_from_process_hook(ctx->rules, process_type_id, hookp_id); + if (func != 0){ + func(ctx, params); + } + } +} + +//// Entry Point //// + +int main(void){ + sy__run_init(); + + Arena arena = arena_alloc((10 << 20)); + + Rules rules = rules_from_symbol_sets(&arena); + + Ctx ctx = {0}; + ctx.rules = &rules; + ctx_new_process(&ctx, SyID(PROCESS_TYPE, SearchWord)); + + for (U32 i = 0; i < 100; i += 1){ + ctx_hook_all_processes(&ctx, SyID(HOOK_POINT, BeginFrame), 0); + + for (U32 viewi = 0; viewi < 2; viewi += 1){ + // imagine each view has an assigned buffer + U32 buffer_id = viewi + 1; + + // render the buffer + HOOK_POINT_TYPE(RenderBuffer) params = { + params.buffer_id = buffer_id + }; + ctx_hook_all_processes(&ctx, SyID(HOOK_POINT, RenderBuffer), ¶ms); + } + + ctx_hook_all_processes(&ctx, SyID(HOOK_POINT, EndFrame), 0); + } + + return(0); +} diff --git a/examples/hooks.h b/examples/hooks.h new file mode 100644 index 0000000..7a0bea9 --- /dev/null +++ b/examples/hooks.h @@ -0,0 +1,163 @@ +#ifndef HOOKS_H +#define HOOKS_H + +//////////////////////////////// +// Little Base Layer + +#include +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; +typedef float F32; +typedef double F64; + +typedef struct String8{ + U8 *str; + U64 size; +} String8; + +#define str8_const(S) {(U8*)(S), (U64)(sizeof(S) - 1)} + +typedef struct Arena{ + void *memory; + U64 pos; + U64 cap; +} Arena; + +static Arena arena_alloc(U64 cap); +static void* arena_push(Arena *arena, U64 size); +static void arena_pop_to(Arena *arena, U64 pos); +static U64 arena_pos(Arena *arena); + +#define push_array(a,T,c) (T*)arena_push((a), sizeof(T)*(c)) + +static void*copy_strided(void *dst, U32 dst_stride, + void *src, U32 src_stride, + U32 count); + +static void*arena_push_symbol_set__raw(Arena *arena, + U32 size, void *src, U32 src_stride, U32 count); + +#define arena_push_symbol_set(ar,S) (SyType(S)*) \ +arena_push_symbol_set__raw((ar), sizeof(SyType(S)), SyFirst(S), SyStride(S), SyCount(S)) + +#define Min(a,b) ((a)<(b))?(a):(b) + +//////////////////////////////// +// Hooks + +//// HOOK_POINT //// + +typedef struct HookPoint{ + String8 name; +} HookPoint; + +#define SYMBOL_SET_DEFINE HOOK_POINT +#define HOOK_POINT_Type HookPoint +#define HOOK_POINT_elf_section ".sy.hookpoint" +#define HOOK_POINT_coff_a_section ".sy$hookpoint_a" +#define HOOK_POINT_coff_m_section ".sy$hookpoint_m" +#define HOOK_POINT_coff_z_section ".sy$hookpoint_z" +#define HOOK_POINT_marker hookpoint +#include "symbol_set.define.h" + +#define HOOK_POINT_STRUCT(N) \ +SyDefine(HOOK_POINT, N) = { .name = str8_const(#N) }; \ +typedef struct HookPointType_##N HookPointType_##N; struct HookPointType_##N + +#define HOOK_POINT_TYPE_DEF(N,T) \ +SyDefine(HOOK_POINT, N) = { .name = str8_const(#N) }; \ +typedef T HookPointType_##N + +#define HOOK_POINT_TYPE(N) HookPointType_##N + +//// PROCESS_TYPE //// + +typedef struct ProcessType{ + String8 name; +} ProcessType; + +#define SYMBOL_SET_DEFINE PROCESS_TYPE +#define PROCESS_TYPE_Type ProcessType +#define PROCESS_TYPE_elf_section ".sy.processtype" +#define PROCESS_TYPE_coff_a_section ".sy$processtype_a" +#define PROCESS_TYPE_coff_m_section ".sy$processtype_m" +#define PROCESS_TYPE_coff_z_section ".sy$processtype_z" +#define PROCESS_TYPE_marker processtype +#include "symbol_set.define.h" + +#define PROCESS_TYPE(N) SyDefine(PROCESS_TYPE, N) = { .name = str8_const(#N) } + +//// PROCESS-HOOK //// + +typedef struct Ctx Ctx; +typedef void HookFunc(Ctx *ctx, void *params); + +typedef struct ProcessHook{ + SY__UAddr process_type_id; + SY__UAddr hook_point_id; + HookFunc *func; +} ProcessHook; + +#define SYMBOL_SET_DEFINE PROCESS_HOOK +#define PROCESS_HOOK_Type ProcessHook +#define PROCESS_HOOK_elf_section ".sy.processhook" +#define PROCESS_HOOK_coff_a_section ".sy$processhook_a" +#define PROCESS_HOOK_coff_m_section ".sy$processhook_m" +#define PROCESS_HOOK_coff_z_section ".sy$processhook_z" +#define PROCESS_HOOK_marker processhook +#include "symbol_set.define.h" + +#define PROCESS_HOOK(PR,HP) \ +static void processhook__##PR##_##HP(Ctx *ctx, HookPointType_##HP *params); \ +SyDefineUnnamed(PROCESS_HOOK) = { \ +.process_type_id = SyRaw(PROCESS_TYPE, PR), \ +.hook_point_id = SyRaw(HOOK_POINT, HP), \ +.func = (HookFunc*)processhook__##PR##_##HP }; \ +static void processhook__##PR##_##HP(Ctx *ctx, HookPointType_##HP *params) + +#if SY__MAIN +SY_INIT(PROCESS_HOOK){ + for (SyEach(PROCESS_HOOK, link)){ + link->process_type_id = SyIDFromRaw(PROCESS_TYPE, link->process_type_id); + link->hook_point_id = SyIDFromRaw(HOOK_POINT, link->hook_point_id); + } +} +#endif + +//// Rules //// + +typedef struct Rules{ + U32 hook_point_count; + U32 process_type_count; + HookPoint *hook_points; + ProcessType *process_types; + + HookFunc **func__process_hook; // [process_type][hook_point]->func +} Rules; + +static Rules rules_from_symbol_sets(Arena *arena); +static HookFunc* rules_func_from_process_hook(Rules *rules, U32 process_type_id, U32 hook_point_id); + +//// CTX //// + +struct Ctx{ + Rules *rules; + + U32 process_type_id[100]; + U32 process_count; +}; + +static void ctx_new_process(Ctx *ctx, U32 process_type_id); +static void ctx_hook_all_processes(Ctx *ctx, U32 hook_id, void *params); + +#endif //HOOKS_H diff --git a/examples/stack_machine.c b/examples/stack_machine.c index eadadfb..bdbec39 100644 --- a/examples/stack_machine.c +++ b/examples/stack_machine.c @@ -459,7 +459,7 @@ sm_state_pop(SM_Ctx *ctx, void *return_data){ if (ctx->stack != 0){ // save return if (return_data != 0){ - SM_State *state = SyAddressFromID(SM_STATE, ctx->stack->state_id); + SM_State *state = SyAddressFromID_Unchecked(SM_STATE, ctx->stack->state_id); SM_Frame *frame = SyAddressFromID(SM_FRAME, state->frame_id); ctx->new_return = push_array(ctx->run_arena, U8, frame->return_size); memcpy(ctx->new_return, return_data, frame->return_size); diff --git a/project.4coder b/project.4coder index d248705..a35e895 100644 --- a/project.4coder +++ b/project.4coder @@ -12,7 +12,7 @@ load_paths = { commands = { .build_with_clang = { .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, - .win = "build_with_clang.bat", .linux = "./build_with_clang.sh stack_machine", + .win = "build_with_clang.bat", .linux = "./build_with_clang.sh hooks", .mac = "./build_with_clang.sh" }, .build_with_cl = { .out = "*compilation*", @@ -26,9 +26,12 @@ commands = { .run_example1 = { .out = "*run*", .footer_panel = false, .save_dirty_files = false, .win = "build\\example1", .linux = "build/example1", .mac = "build/example1" }, + .run_hooks = { .out = "*run*", .footer_panel = false, .save_dirty_files = false, + .win = "build\\hooks", .linux = "build/hooks", .mac = "build/hooks" + }, }; fkey_command = { .F1 = "build_with_clang", - .F2 = "run_example1", + .F2 = "run_hooks", };