From d026e93405655129e46debfca2124ee132e9b134 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sun, 16 Nov 2025 20:52:57 -0600 Subject: build config + more plugin stuff --- src/pipeline.c | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 src/pipeline.c (limited to 'src/pipeline.c') diff --git a/src/pipeline.c b/src/pipeline.c new file mode 100644 index 0000000..8b54cca --- /dev/null +++ b/src/pipeline.c @@ -0,0 +1,316 @@ +#include "pipeline.h" + +#include +#include +#include +#include +#include + +struct ptx__pipeline_stage_tag +{ + struct ptx_pipeline_stage_funcs impl; + char *name; + void *user; +}; + +struct ptx__pipeline_ctx_tag +{ + ptx_pipeline_t *pipeline; + size_t curidx; +}; + +struct ptx__pipeline_tag +{ + size_t stages_len, stages_cap; + struct ptx__pipeline_stage_tag *stages; + bool handling; +}; + +/* returns: + * -1 : allocation failed + * 0 : nothing was allocated + * 1 : pipeline grown */ +static int ptx__pipeline_reserve(ptx_pipeline_t *pl, size_t req) +{ + assert(pl); + assert(req < SSIZE_MAX); + + if (req < pl->stages_cap) return 0; + + struct ptx__pipeline_stage_tag *temp = realloc(pl->stages, req * sizeof(struct ptx__pipeline_stage_tag)); + if (!temp) return -1; + + pl->stages = temp; + pl->stages_cap = req; + + return 1; +} + +/* allocates a new pipeline, with space for `initial_size' new stages. */ +PTX_API ptx_pipeline_t *ptx_pipeline_new(size_t initial_size) +{ + ptx_pipeline_t *pl = malloc(sizeof(ptx_pipeline_t)); + if (!pl) return NULL; + + pl->stages_len = 0; + pl->stages_cap = 0; + pl->stages = NULL; + pl->handling = false; + + ptx__pipeline_reserve(pl, initial_size < 1 ? 1 : initial_size); + + return pl; +} + +static void ptx__cleanup_pipeline_stage(struct ptx__pipeline_stage_tag *stage) +{ + assert(stage); + + if (stage->impl.cleanup) { + (*stage->impl.cleanup)(stage->name, stage->user); + } + + free(stage->name); + + /* clobber the structure just cuz */ + memset(stage, 0, sizeof(*stage)); +} + +/* frees an existing pipeline stage. note that this will call the cleanup function in the stages as well. */ +PTX_API void ptx_pipeline_free(ptx_pipeline_t *pl) +{ + if (!pl) return; + + for (size_t idx = 0; idx < pl->stages_len; ++idx) { + ptx__cleanup_pipeline_stage(pl->stages + idx); + } + + free(pl->stages); + free(pl); +} + +static int ptx__init_pipeline_stage( + struct ptx__pipeline_stage_tag *PTX_RESTRICT pstage, + const struct ptx_pipeline_stage_funcs *PTX_RESTRICT stage_impl, + const char *name, + va_list setup_args) +{ + assert(pstage); + assert(stage_impl); + assert(name); + + char *namedup = NULL; + void *user = NULL; + + /* I'd rather try to duplicate the name first. Otherwise, we'd have to clean up the + * stage's user data right after initializing it. */ + namedup = strdup(name); + if (!namedup) goto cleanup; + + if (stage_impl->init && (*stage_impl->init)(name, &user, setup_args) < 0) { + /* setup failed :((( */ + goto cleanup; + } + + /* yay it worked :) */ + pstage->user = user; + pstage->name = namedup; + memcpy(&pstage->impl, stage_impl, sizeof(*stage_impl)); + + return 0; + +cleanup: + free(namedup); + return -1; +} + +/* this will grow a pipeline to fit a new stage. + * this function also shifts entries in `pl->stages' such that `to_add' is an empty stage. */ +static int ptx__pipeline_prepare_insert_stage_at(ptx_pipeline_t *pipeline, size_t to_add) +{ + assert(to_add <= pipeline->stages_len); + if (ptx__pipeline_reserve(pipeline, pipeline->stages_len + 1) < 0) { + return -1; + } + + /* don't perform the move if we wouldn't be moving anything. */ + if (to_add < pipeline->stages_len) { + memmove(pipeline->stages + to_add + 1, + pipeline->stages + to_add, + sizeof(struct ptx__pipeline_stage_tag) * (pipeline->stages_len - to_add)); + } + + /* increment the length of the array here for consistency. + * NOTE: this function still leaves the pipeline in an inconsistent state: + * there is an uninitialized stage at `to_add' :( */ + ++pipeline->stages_len; + + return 0; +} + +static ssize_t ptx__pipeline_find_stage(ptx_pipeline_t *ptx, const char *name) +{ + assert(ptx); + assert(name); + + for (size_t idx = 0; idx < ptx->stages_len; ++idx) { + if (!strcmp(ptx->stages[idx].name, name)) { + assert(idx <= SSIZE_MAX); + return (ssize_t)idx; + } + } + + return -1; +} + +static int ptx__pipeline_add_at( + ptx_pipeline_t *pipeline, + const struct ptx_pipeline_stage_funcs *stage_impl, + const char *name, + size_t idx, + va_list init_args) +{ + struct ptx__pipeline_stage_tag temp_stage; + memset(&temp_stage, 0, sizeof(temp_stage)); + + /* first, initialize the stage itself */ + if (ptx__init_pipeline_stage(&temp_stage, stage_impl, name, init_args) < 0) { + return -1; + } + + /* next, grow the pipeline to fit the new stage */ + if (ptx__pipeline_prepare_insert_stage_at(pipeline, idx) < 0) { + /* uh oh, it failed. clean up the stage we just initialized */ + ptx__cleanup_pipeline_stage(&temp_stage); + return -1; + } + + /* finally, put the stage into the array */ + memcpy(pipeline->stages + idx, &temp_stage, sizeof(temp_stage)); + return 0; +} + +/* adds `stage' with `name' to `pipeline' after `after' * + * returns: number of stages added to pipeline (0 or 1), or -1 if there was an allocation or initialization error. */ +PTX_API int ptx_pipeline_add_after( + ptx_pipeline_t *pipeline, + const struct ptx_pipeline_stage_funcs *stage, + const char *name, + const char *after, + ...) +{ + assert(pipeline); + assert(!pipeline->handling); + + size_t idx; + + if (after) { + ssize_t sidx = ptx__pipeline_find_stage(pipeline, after); + if (sidx < 0) return 0; + + idx = (size_t)(sidx + 1); /* after */ + } else { + idx = pipeline->stages_len; + } + + va_list init_args; + va_start(init_args, after); + + int ret = ptx__pipeline_add_at(pipeline, stage, name, idx, init_args); + + va_end(init_args); + return ret; +} + +/* adds `stage' with `name' to `pipeline' before `before' + * returns: number of stages added to pipeline (0 or 1), or -1 if there was an allocation or initialization error. */ +PTX_API int ptx_pipeline_add_before( + ptx_pipeline_t *pipeline, + const struct ptx_pipeline_stage_funcs *stage, + const char *name, + const char *before, + ...) +{ + assert(pipeline); + assert(!pipeline->handling); + + size_t idx; + + if (before) { + ssize_t sidx = ptx__pipeline_find_stage(pipeline, before); + if (sidx < 0) return 0; + + idx = (size_t)sidx; /* before */ + } else { + idx = 0; + } + + va_list init_args; + va_start(init_args, before); + + int ret = ptx__pipeline_add_at(pipeline, stage, name, idx, init_args); + + va_end(init_args); + return ret; +} + +/* returns: number of stages removed from pipeline */ +PTX_API int ptx_pipeline_remove_stage(ptx_pipeline_t *pipeline, const char *name) +{ + assert(pipeline); + assert(!pipeline->handling); + + ssize_t idx = ptx__pipeline_find_stage(pipeline, name); + if (idx < 0) return 0; + + ptx__cleanup_pipeline_stage(pipeline->stages + idx); + memmove(pipeline->stages + idx, pipeline->stages + idx + 1, sizeof(pipeline->stages[0]) * (pipeline->stages_len - (size_t)idx - 1)); + + --pipeline->stages_len; + return 1; +} + +PTX_API void **ptx_pipeline_ctx_get_user(const ptx_pipeline_ctx_t *ctx) +{ + assert(ctx); + return &ctx->pipeline->stages[ctx->curidx].user; +} + +PTX_API const char *ptx_pipeline_ctx_get_name(const ptx_pipeline_ctx_t *ctx) +{ + assert(ctx); + return ctx->pipeline->stages[ctx->curidx].name; +} + +PTX_API int ptx_pipeline_ctx_next(const ptx_pipeline_ctx_t *ctx, const void *nextdata, size_t nextsize) +{ + assert(ctx->curidx + 1 < ctx->pipeline->stages_len); + + ptx_pipeline_ctx_t new_ctx; + new_ctx.pipeline = ctx->pipeline; + new_ctx.curidx = ctx->curidx + 1; + + assert(new_ctx.pipeline->stages[new_ctx.curidx].impl.handler); + + return (*new_ctx.pipeline->stages[new_ctx.curidx].impl.handler)(&new_ctx, nextdata, nextsize); +} + +PTX_API int ptx_pipeline_handle(ptx_pipeline_t *pipeline, const void *data, size_t sz) +{ + assert(pipeline); + assert(!pipeline->handling); + + if (pipeline->stages_len == 0) return 0; /* pipeline does nothing... */ + + pipeline->handling = true; + + ptx_pipeline_ctx_t ctx; + ctx.pipeline = pipeline; + ctx.curidx = 0; + + assert(pipeline->stages[0].impl.handler); + int ret = (*pipeline->stages[0].impl.handler)(&ctx, data, sz); + + pipeline->handling = 0; + return ret; +} -- cgit v1.2.3-70-g09d2