IOXD_SLICE(3)libioxd Programmer's ManualIOXD_SLICE(3)

NAME

ioxd/slice.h - a slice, bytes with a length, and a key/value pair of them: what every request carries. Compare and convert them without copying; parse a query string or a form body.

SYNOPSIS

#include <ioxd.h>

typedef struct { const char *p; size_t len; } ioxd_slice;
typedef struct { ioxd_slice key, value; } ioxd_kv;
bool       ioxd_slice_eq         (ioxd_slice s, const char *cstr);
bool       ioxd_slice_eq_ci      (ioxd_slice s, const char *cstr);
bool       ioxd_slice_starts_with(ioxd_slice s, const char *prefix);
bool       ioxd_slice_ends_with  (ioxd_slice s, const char *suffix);
ioxd_slice ioxd_slice_trim       (ioxd_slice s);
bool ioxd_cstr(ioxd_slice s, char *buf, size_t cap);
bool ioxd_to_int   (ioxd_slice s, int      *out);
bool ioxd_to_i64   (ioxd_slice s, int64_t  *out);
bool ioxd_to_u64   (ioxd_slice s, uint64_t *out);
bool ioxd_to_double(ioxd_slice s, double   *out);
bool ioxd_to_bool  (ioxd_slice s, bool     *out);
size_t ioxd_kv_parse(const char *text, size_t len, ioxd_kv *out, size_t cap, char *arena, size_t arena_cap, bool *truncated);

DESCRIPTION

typedef struct { const char *p; size_t len; } ioxd_slice;

A slice: pointer + length, the C span. Not NUL-terminated.

typedef struct { ioxd_slice key, value; } ioxd_kv;

One key/value pair of slices: a header, a query parameter, a route parameter.

slices

bool       ioxd_slice_eq         (ioxd_slice s, const char *cstr);
bool       ioxd_slice_eq_ci      (ioxd_slice s, const char *cstr);
bool       ioxd_slice_starts_with(ioxd_slice s, const char *prefix);
bool       ioxd_slice_ends_with  (ioxd_slice s, const char *suffix);
ioxd_slice ioxd_slice_trim       (ioxd_slice s);

Everything a request carries is a slice: bytes with a length, not NUL-terminated, valid until the handler returns. These compare and convert one without copying it.

ioxd_slice_eq
exact
ioxd_slice_eq_ci
ASCII case-insensitive
ioxd_slice_trim
no leading/trailing space, tab, CR, LF
bool ioxd_cstr(ioxd_slice s, char *buf, size_t cap);

A NUL-terminated copy in buf, for whatever wants a C string. False when it did not fit: buf then holds what fit, still terminated (cap 0 writes nothing) - and false when the slice holds a NUL of its own, which would end the C string early ("secret.txt%00.png" is not a PNG).

bool ioxd_to_int   (ioxd_slice s, int      *out);
bool ioxd_to_i64   (ioxd_slice s, int64_t  *out);
bool ioxd_to_u64   (ioxd_slice s, uint64_t *out);
bool ioxd_to_double(ioxd_slice s, double   *out);
bool ioxd_to_bool  (ioxd_slice s, bool     *out);

Conversions. The whole slice must be the value - nothing around it, nothing after it - and a number that does not fit the type fails. On failure *out is left alone and false comes back, so "0" and "not a number" cannot be confused. Integers: an optional '-' and decimal digits. Doubles: also a fraction and an exponent ("2.5", ".5", "1e-3"); never inf, nan or hex; too large fails, too small rounds towards zero. Booleans: true/false, 1/0, yes/no, on/off, any case.

size_t ioxd_kv_parse(const char *text, size_t len, ioxd_kv *out, size_t cap, char *arena, size_t arena_cap,
                     bool *truncated);

Parse "k=v&k2=v2" - a query string, a form body - into out, up to cap pairs. Keys and values that need it ('+', %XX) are decoded into arena and point there; the rest are views of s. A malformed %XX and %00 stay as written. Returns the pair count; *truncated (may be NULL) is set when a pair was left out - past cap, or not fitting the arena - so the caller can refuse the request rather than act on part of it. Every pair is returned, duplicates included, in order: pick a policy (first or last) and keep to it.

EXAMPLES

Strict conversions: the whole slice is the value, or the call fails and *out is untouched:

int64_t id;
double  price;
bool    on;
if (!ioxd_to_i64(ctx->req.route_params[0].value, &id))     /* "42", "-7"; not "42x", not " 42" */
    ...
if (ioxd_to_double(v, &price) && ioxd_to_bool(w, &on))      /* "2.5", "1e-3"; "yes", "off" */
    ...

A form body parsed like a query string:

ioxd_slice body = ioxd_body_all(ctx);
ioxd_kv    form[8];
char       arena[512];
bool       truncated;
size_t n = ioxd_kv_parse(body.p, body.len, form, 8, arena, sizeof arena, &truncated);
if (truncated) { ctx->res.status = 400; return; }        /* never act on part of it */
for (size_t i = 0; i < n; i++)
    if (ioxd_slice_eq(form[i].key, "name"))
        ioxd_printf(ctx, "hello %.*s\n", (int)form[i].value.len, form[i].value.p);

SEE ALSO

ioxd_config(3), ioxd_http(3), ioxd_router(3), ioxd_json(3), ioxd_pipe(3), ioxd_tls(3), ioxd_examples(7), ioxd(7)

libioxd 0.1.02026-09-09IOXD_SLICE(3)