new example 'hooks' shows arena_push_symbol_set and a way to organize a hookable system
parent
25be3d41bc
commit
69b419b086
|
|
@ -13,4 +13,8 @@ if "%1" == "" (
|
||||||
if "%1" == "stack_machine" (
|
if "%1" == "stack_machine" (
|
||||||
cl -Festack_machine %code%\examples\stack_machine.c %opts% %inc% %linkopts%
|
cl -Festack_machine %code%\examples\stack_machine.c %opts% %inc% %linkopts%
|
||||||
)
|
)
|
||||||
|
if "%1" == "hooks" (
|
||||||
|
cl -Fehooks %code%\examples\hooks.c %opts% %inc% %linkopts%
|
||||||
|
)
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ if "%1" == "" (
|
||||||
if "%1" == "stack_machine" (
|
if "%1" == "stack_machine" (
|
||||||
clang -o stack_machine %code%\examples\stack_machine.c %opts% %inc% %linkopts%
|
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
|
popd
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,8 @@ elif [[ "$1" == "stack_machine" ]]; then
|
||||||
clang -c $code/examples/stack_machine.c $opts $inc
|
clang -c $code/examples/stack_machine.c $opts $inc
|
||||||
./symbol_set.ld_meta stack_machine.o
|
./symbol_set.ld_meta stack_machine.o
|
||||||
clang -o stack_machine $code/examples/stack_machine.c $opts $inc -T sy.ld
|
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
|
fi
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
** C Scripting - Hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SY__MAIN 1
|
||||||
|
#include "symbol_set.h"
|
||||||
|
#include "symbol_set.init.h"
|
||||||
|
|
||||||
|
#include "hooks.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
#ifndef HOOKS_H
|
||||||
|
#define HOOKS_H
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Little Base Layer
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
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
|
||||||
|
|
@ -459,7 +459,7 @@ sm_state_pop(SM_Ctx *ctx, void *return_data){
|
||||||
if (ctx->stack != 0){
|
if (ctx->stack != 0){
|
||||||
// save return
|
// save return
|
||||||
if (return_data != 0){
|
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);
|
SM_Frame *frame = SyAddressFromID(SM_FRAME, state->frame_id);
|
||||||
ctx->new_return = push_array(ctx->run_arena, U8, frame->return_size);
|
ctx->new_return = push_array(ctx->run_arena, U8, frame->return_size);
|
||||||
memcpy(ctx->new_return, return_data, frame->return_size);
|
memcpy(ctx->new_return, return_data, frame->return_size);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ load_paths = {
|
||||||
commands = {
|
commands = {
|
||||||
.build_with_clang = { .out = "*compilation*",
|
.build_with_clang = { .out = "*compilation*",
|
||||||
.footer_panel = true, .save_dirty_files = true,
|
.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"
|
.mac = "./build_with_clang.sh"
|
||||||
},
|
},
|
||||||
.build_with_cl = { .out = "*compilation*",
|
.build_with_cl = { .out = "*compilation*",
|
||||||
|
|
@ -26,9 +26,12 @@ commands = {
|
||||||
.run_example1 = { .out = "*run*", .footer_panel = false, .save_dirty_files = false,
|
.run_example1 = { .out = "*run*", .footer_panel = false, .save_dirty_files = false,
|
||||||
.win = "build\\example1", .linux = "build/example1", .mac = "build/example1"
|
.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 = {
|
fkey_command = {
|
||||||
.F1 = "build_with_clang",
|
.F1 = "build_with_clang",
|
||||||
.F2 = "run_example1",
|
.F2 = "run_hooks",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue