summaryrefslogtreecommitdiffstats
path: root/src/pipeline.c
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-11-16 20:52:57 -0600
committerLibravatar bigfoot547 <[email protected]>2025-11-16 20:52:57 -0600
commitd026e93405655129e46debfca2124ee132e9b134 (patch)
tree8adf037dd69c4226ef9a5d2a3dd8fa63eb783761 /src/pipeline.c
parent[wip] plugin stuff (diff)
build config + more plugin stuff
Diffstat (limited to 'src/pipeline.c')
-rw-r--r--src/pipeline.c316
1 files changed, 316 insertions, 0 deletions
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 <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <limits.h>
+
+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;
+}