From 06322a708cb7fd99f976e47949b243846a5b947a Mon Sep 17 00:00:00 2001 From: MDA2AV Date: Mon, 7 Sep 2026 21:30:36 +0100 Subject: [PATCH 1/2] libioma: build with gcc 14 (the library is C23 now); handler on the context API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libioma ec5b328 is C23 and its Makefile requires gcc 14 or newer, so the build stage installs gcc-14 and builds both the library and the handler with it: the fat LTO objects and the final -flto link have to come from the same compiler. This also carries the entry state that was pushed to the merged #1463 branch after the merge and never landed in main: the handler on the req/res context API (params read directly, body on demand, digits written into the reply slab) and the LTO build. Pin bumped to ec5b328. validate.sh libioma: all passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013wYnJvEFUjKEpGLkyTLt9P --- frameworks/libioma/Dockerfile | 15 +++++---- frameworks/libioma/README.md | 2 +- frameworks/libioma/main.c | 63 ++++++++--------------------------- 3 files changed, 23 insertions(+), 57 deletions(-) diff --git a/frameworks/libioma/Dockerfile b/frameworks/libioma/Dockerfile index c5d767872..ba062c848 100644 --- a/frameworks/libioma/Dockerfile +++ b/frameworks/libioma/Dockerfile @@ -1,20 +1,21 @@ FROM ubuntu:24.04 AS build RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc make curl ca-certificates libc6-dev \ + gcc-14 make curl ca-certificates libc6-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /app -# Build libioma from its own repo, pinned to a commit for reproducibility (the h2o pattern: fetch -# upstream at a fixed SHA and build it here, tuned for this CPU). No liburing — libioma issues the -# io_uring syscalls directly. Only the baseline handler lives in this entry. -ARG LIBIOMA_VERSION=263905f98b3c6f9200ed11ddbfaf0f5cddcecc9d +# Build libioma from its own repo, pinned to a commit (the h2o pattern: fetch upstream at a fixed +# SHA and build it here, tuned for this CPU). No liburing: libioma issues the io_uring syscalls +# directly. Only the baseline handler lives in this entry. Fat LTO objects for the library and +# -flto on the handler link, so the handler inlines into the engine. +ARG LIBIOMA_VERSION=ec5b32875d244b7477b00a04969c93e84e8fd368 RUN mkdir -p /tmp/libioma && \ curl -LSs "https://github.com/MDA2AV/libioma/archive/${LIBIOMA_VERSION}.tar.gz" \ | tar --strip-components=1 -xz -C /tmp/libioma && \ - make -C /tmp/libioma libioma.a CFLAGS="-O3 -march=native" + make -C /tmp/libioma libioma.a CC=gcc-14 CFLAGS="-O3 -march=native -flto -ffat-lto-objects" COPY main.c . -RUN gcc -O3 -march=native -Wall -std=gnu11 -pthread \ +RUN gcc-14 -O3 -march=native -flto -Wall -std=gnu11 -pthread \ -I/tmp/libioma/include \ main.c /tmp/libioma/libioma.a -o server -pthread diff --git a/frameworks/libioma/README.md b/frameworks/libioma/README.md index 31bfcbd8c..9b97d6e2b 100644 --- a/frameworks/libioma/README.md +++ b/frameworks/libioma/README.md @@ -7,7 +7,7 @@ state machine. ## Stack -- **Language:** C (GCC, `-O2 -march=native`) +- **Language:** C23 (gcc 14, `-O3 -march=native -flto`) - **Engine:** raw `io_uring` syscalls — no liburing. Multishot accept and multishot recv over per-worker provided buffer rings; `SINGLE_ISSUER | DEFER_TASKRUN | NO_SQARRAY`. - **Architecture:** thread-per-core, shared-nothing. One ring, one `SO_REUSEPORT` listener and one diff --git a/frameworks/libioma/main.c b/frameworks/libioma/main.c index e6456f134..d51696232 100644 --- a/frameworks/libioma/main.c +++ b/frameworks/libioma/main.c @@ -1,59 +1,25 @@ /* - * HttpArena baseline handler for ioma. + * HttpArena baseline handler for libioma. * - * baseline profile: GET/POST /baseline11?a=..&b=.. — sum the query parameter values, and on POST - * add the request body value too. The body arrives as Content-Length or Transfer-Encoding: chunked; - * ioma decodes both, so the same handler serves all three request shapes the profile rotates. + * baseline profile: GET/POST /baseline11?a=..&b=.. - sum the query parameter values, and on POST + * add the request body value too. libioma hands the query already split into key/value slices; + * the body is read on demand (Content-Length or chunked, decoded). The digits are written straight + * into the reply slab. */ #include #include -/* Sum the integer values of the query parameters, e.g. "a=13&b=42" -> 55. */ -static long sum_query(const char *q, size_t n) -{ - long sum = 0; - size_t i = 0; - while (i < n) { - while (i < n && q[i] != '=') i++; /* to this parameter's '=' */ - if (i >= n) break; - i++; /* past '=' */ - - long v = 0; - int any = 0; - while (i < n && q[i] != '&') { - if (q[i] >= '0' && q[i] <= '9') { - v = v * 10 + (q[i] - '0'); - any = 1; - } - i++; - } - if (any) sum += v; - if (i < n && q[i] == '&') i++; - } - return sum; -} - -/* Leading integer of a body like "20". */ -static long body_int(const char *b, size_t n) -{ - long v = 0; - for (size_t i = 0; i < n; i++) { - if (b[i] < '0' || b[i] > '9') break; - v = v * 10 + (b[i] - '0'); - } - return v; -} - -/* GET / POST /baseline11 - the summed response, formatted straight into the request scratch. */ -static ioma_response baseline11(ioma_request *req) +static void baseline11(ioma_ctx *c) { long sum = 0; - if (req->query_len) sum += sum_query(req->query, req->query_len); - if (req->body_len) sum += body_int(req->body, req->body_len); + for (size_t i = 0; i < c->req.n_params; i++) + sum += ioma_slice_int(c->req.params[i].value); + if (c->req.content_length || c->req.chunked) + sum += ioma_slice_int(ioma_body(c)); - /* itoa into scratch (alive until the reply is sent) - no snprintf on the hot path */ - char *p = req->scratch; + /* itoa straight into the reply slab - no snprintf, no copy */ + char *out = c->res.buf + c->res.len; char tmp[24]; int t = 0; unsigned long u = (unsigned long)(sum < 0 ? 0 : sum); @@ -62,9 +28,8 @@ static ioma_response baseline11(ioma_request *req) u /= 10; } while (u); for (int i = 0; i < t; i++) - p[i] = tmp[t - 1 - i]; - - return ioma_bytes(200, "text/plain", req->scratch, (size_t)t); + out[i] = tmp[t - 1 - i]; + c->res.len += (size_t)t; } int main(int argc, char **argv) From 3bfb235fa8fe1d4c76b7588c4489a4cecae8c01c Mon Sep 17 00:00:00 2001 From: MDA2AV Date: Mon, 7 Sep 2026 22:16:51 +0100 Subject: [PATCH 2/2] libioma: handler on the strict conversions (bump pin) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libioma b027f05 replaces the lenient ioma_slice_int with typed conversions that fail instead of guessing; the handler sums with ioma_to_i64 and trims the body first. validate.sh libioma: all passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013wYnJvEFUjKEpGLkyTLt9P --- frameworks/libioma/Dockerfile | 2 +- frameworks/libioma/main.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frameworks/libioma/Dockerfile b/frameworks/libioma/Dockerfile index ba062c848..2c97ca9bb 100644 --- a/frameworks/libioma/Dockerfile +++ b/frameworks/libioma/Dockerfile @@ -8,7 +8,7 @@ WORKDIR /app # SHA and build it here, tuned for this CPU). No liburing: libioma issues the io_uring syscalls # directly. Only the baseline handler lives in this entry. Fat LTO objects for the library and # -flto on the handler link, so the handler inlines into the engine. -ARG LIBIOMA_VERSION=ec5b32875d244b7477b00a04969c93e84e8fd368 +ARG LIBIOMA_VERSION=b027f05360b8ae5192bd73133437663cd645bac1 RUN mkdir -p /tmp/libioma && \ curl -LSs "https://github.com/MDA2AV/libioma/archive/${LIBIOMA_VERSION}.tar.gz" \ | tar --strip-components=1 -xz -C /tmp/libioma && \ diff --git a/frameworks/libioma/main.c b/frameworks/libioma/main.c index d51696232..47c77905b 100644 --- a/frameworks/libioma/main.c +++ b/frameworks/libioma/main.c @@ -12,11 +12,12 @@ static void baseline11(ioma_ctx *c) { - long sum = 0; + int64_t sum = 0, v; for (size_t i = 0; i < c->req.n_params; i++) - sum += ioma_slice_int(c->req.params[i].value); - if (c->req.content_length || c->req.chunked) - sum += ioma_slice_int(ioma_body(c)); + if (ioma_to_i64(c->req.params[i].value, &v)) + sum += v; + if ((c->req.content_length || c->req.chunked) && ioma_to_i64(ioma_slice_trim(ioma_body(c)), &v)) + sum += v; /* itoa straight into the reply slab - no snprintf, no copy */ char *out = c->res.buf + c->res.len;