semq-sys 1.0.0

Raw FFI bindings to the SEMQ native library.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
 * Copyright (c) 2026 The SEMQ Group Inc.
 * Licensed under the PolyForm Noncommercial License 1.0.0. See LICENSE.md.
 */

/**
 * @file semq.h
 * @brief SEMQ core: the public C ABI every language binding wraps.
 *
 * The core owns every rule that determines bytes or verdicts: the operator
 * mappings, the canonical forms, the file format, the digests, the diff and
 * the floor arithmetic. A binding validates its own representation, maps
 * errors and does I/O; it never re-implements a rule declared here.
 *
 * Every function that can fail returns a ::semq_status_t and, when the
 * caller passes a non-NULL ::semq_error_t, fills it with the status, the
 * failing row and field where they apply, and a NUL-terminated message.
 * No function keeps thread-local error state.
 *
 * Handles (::semq_codec_t, ::semq_encoding_t, ::semq_diff_t) are immutable
 * after construction and safe to share between threads. Every accessor
 * that returns a pointer into a handle returns memory owned by that handle,
 * valid until the handle is freed.
 * A call borrows its arguments for its duration; the caller keeps them alive
 * and does not mutate inputs or free a borrowed handle concurrently. Each
 * returned owning handle is released exactly once with its matching free;
 * free(NULL) is a no-op. Diff retains its two inputs independently.
 *
 * Pointer preconditions (unless an individual function says otherwise):
 * - Required handles, config/ids PODs and output pointers are non-NULL.
 * - err may be NULL. Successful status calls clear a supplied error report.
 * - Arrays cover their declared element count; typed pointers have their C
 *   type's alignment. File images and byte buffers need no extra alignment.
 * - Zero-length arrays may be NULL. With ids.n == 0 all ID payload pointers,
 *   rows/vectors and decode/unpack outputs may be NULL; unused ID-kind
 *   pointers are ignored. A zero-length manifest value may be NULL.
 * - Mutable outputs (including err and handle out-pointers) do not overlap
 *   each other, inputs, or memory borrowed from a handle.
 * - Handle constructors set *out = NULL before fallible work and transfer ownership
 *   only on success. Other outputs are unspecified on failure unless stated.
 *   A config getter borrows its POD; it is never passed to a free function.
 * C cannot establish the physical capacity of a caller's arbitrary pointer.
 * Counts and capacities supplied by bindings must describe actual buffers.
 *
 * All multi-byte integers in canonical forms and files are little-endian.
 * The core targets little-endian hosts only.
 */

#ifndef SEMQ_H
#define SEMQ_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#if defined(_WIN32)
  #define SEMQ_API __declspec(dllexport)
#else
  #define SEMQ_API __attribute__((visibility("default")))
#endif

/* -------------------------------------------------------------------------- */
/*  Limits and constants                                                      */
/* -------------------------------------------------------------------------- */

/** File format version written and accepted by this core. */
#define SEMQ_FILE_VERSION 2u
/** Operator rule revision written into every config (`p2`). */
#define SEMQ_RULE_REVISION 0u
/** Size of the canonical config form in bytes. */
#define SEMQ_CONFIG_BYTES 13u
/** Size of each identity (`content_digest`, `state_id`) in bytes. */
#define SEMQ_DIGEST_BYTES 32u
/** Largest accepted vector dimension. */
#define SEMQ_MAX_DIM 65536u
/** Largest `utf8` id, in bytes. Ids are at least one byte. */
#define SEMQ_ID_MAX_BYTES 4096u
/** Manifest limits. */
#define SEMQ_MANIFEST_MAX_PAIRS 4096u
#define SEMQ_MANIFEST_KEY_MAX 256u
#define SEMQ_MANIFEST_VALUE_MAX 65536u
#define SEMQ_MANIFEST_SECTION_MAX (16u * 1024u * 1024u)
/** Sentinel for "no row" / "no field" in ::semq_error_t and index lookups. */
#define SEMQ_NONE UINT64_MAX

/** Operators. Values are pinned: they appear in the canonical config form. */
typedef enum {
    SEMQ_ORBIT = 0,
    SEMQ_PHASE = 1,
    SEMQ_QUANT = 2
} semq_operator_t;

/** Identifier kinds. Values are pinned: they appear in files. */
typedef enum {
    SEMQ_ID_U64  = 0,
    SEMQ_ID_UTF8 = 1
} semq_id_kind_t;

/** Status codes. Each maps to exactly one host error type. */
typedef enum {
    SEMQ_OK                = 0,
    SEMQ_ERR_INVALID_INPUT = 1,  /**< an argument violates its contract */
    SEMQ_ERR_INCOMPATIBLE  = 2,  /**< two states differ in config or id kind */
    SEMQ_ERR_FORMAT        = 3,  /**< a file image is not a valid v2 image */
    SEMQ_ERR_INTEGRITY     = 4,  /**< a file image's digest does not match */
    SEMQ_ERR_UNSUPPORTED   = 5,  /**< the operation or FP environment is not supported */
    SEMQ_ERR_NOMEM         = 6,  /**< an allocation failed */
    SEMQ_ERR_INTERNAL      = 7   /**< a defect in the core; report it */
} semq_status_t;

/** Which integrity check failed on load. */
typedef enum {
    SEMQ_WHICH_NONE    = 0,
    SEMQ_WHICH_CONTENT = 1,
    SEMQ_WHICH_STATE   = 2
} semq_which_t;

/** File sections named by `field` in ::SEMQ_ERR_FORMAT errors. */
typedef enum {
    SEMQ_SECTION_FRAMING  = 0,
    SEMQ_SECTION_CONFIG   = 1,
    SEMQ_SECTION_SIZES    = 2,
    SEMQ_SECTION_IDS      = 3,
    SEMQ_SECTION_MANIFEST = 4,
    SEMQ_SECTION_FOOTER   = 5,
    SEMQ_SECTION_ROWS     = 6
} semq_section_t;

/** Config fields named by `field` in ::SEMQ_ERR_INVALID_INPUT errors raised
 *  by the config constructors. */
typedef enum {
    SEMQ_FIELD_OPERATOR = 0,
    SEMQ_FIELD_DIM      = 1,
    SEMQ_FIELD_P1       = 2,  /**< scale, sectors or bins */
    SEMQ_FIELD_P2       = 3
} semq_config_field_t;

/**
 * Error report. `status` is the returned status; `which` is set only for
 * ::SEMQ_ERR_INTEGRITY; `row` and `field` are ::SEMQ_NONE when they do not
 * apply. `field` is a unit or column index for input errors, a config
 * field for config errors, and a ::semq_section_t for format errors.
 * `message` is always NUL-terminated and never contains input data.
 */
typedef struct {
    uint32_t status;
    uint32_t which;
    uint64_t row;
    uint64_t field;
    char     message[128];
} semq_error_t;

/* -------------------------------------------------------------------------- */
/*  CodecConfig                                                               */
/* -------------------------------------------------------------------------- */

/**
 * A codec configuration. `op` is a ::semq_operator_t; `p1` is `scale`
 * (orbit), `sectors` (phase) or `bins` (quant); `p2` is the rule revision.
 * Build one with the constructors below; a struct filled by hand must pass
 * ::semq_config_validate before use. Two configs are equal iff their 13
 * canonical bytes are equal.
 */
typedef struct {
    uint32_t op;
    uint32_t dim;
    uint32_t p1;
    uint32_t p2;
} semq_config_t;

SEMQ_API semq_status_t semq_config_orbit(uint32_t dim, uint32_t scale,
                                         semq_config_t* out, semq_error_t* err);
SEMQ_API semq_status_t semq_config_phase(uint32_t dim, uint32_t sectors,
                                         semq_config_t* out, semq_error_t* err);
SEMQ_API semq_status_t semq_config_quant(uint32_t dim, uint32_t bins,
                                         semq_config_t* out, semq_error_t* err);
SEMQ_API semq_status_t semq_config_validate(const semq_config_t* cfg, semq_error_t* err);

/** Write the 13-byte canonical form. `cfg` must be valid. */
SEMQ_API void semq_config_to_bytes(const semq_config_t* cfg, uint8_t out[13]);
/** Parse a 13-byte canonical form; ::SEMQ_ERR_INVALID_INPUT when invalid. */
SEMQ_API semq_status_t semq_config_from_bytes(const uint8_t in[13], semq_config_t* out,
                                              semq_error_t* err);
/** Derived quantities. `cfg` must be valid. */
SEMQ_API uint32_t semq_config_bytes_per_vector(const semq_config_t* cfg);
SEMQ_API uint32_t semq_config_units_per_row(const semq_config_t* cfg);
/** quant only: `(float)(2.0 / sqrt((double)dim))`; 0 for other operators.
 *  The caller must be in FE_TONEAREST, as for codec construction. */
SEMQ_API float semq_config_max_magnitude(const semq_config_t* cfg);
/** 1 when the two configs have the same canonical bytes. */
SEMQ_API int semq_config_equal(const semq_config_t* a, const semq_config_t* b);

/* -------------------------------------------------------------------------- */
/*  Codec                                                                     */
/* -------------------------------------------------------------------------- */

typedef struct semq_codec    semq_codec_t;
typedef struct semq_encoding semq_encoding_t;
typedef struct semq_diff     semq_diff_t;

/** One manifest pair. Keys and values are UTF-8 bytes, not NUL-terminated. */
typedef struct {
    const uint8_t* key;
    uint32_t       key_len;
    const uint8_t* value;
    uint32_t       value_len;
} semq_pair_t;

/**
 * Identifiers of `n` rows, in input order (any order; the core sorts).
 * For ::SEMQ_ID_U64 pass `u64` (n entries). For ::SEMQ_ID_UTF8 pass
 * `utf8_offsets` (n + 1 entries, `offsets[0] = 0`, non-decreasing) and
 * `utf8_bytes` (`offsets[n]` bytes): id `i` is `bytes[offsets[i] .. offsets[i+1])`.
 */
typedef struct {
    uint32_t        kind;
    uint64_t        n;
    const uint64_t* u64;
    const uint64_t* utf8_offsets;
    const uint8_t*  utf8_bytes;
} semq_ids_t;

/** Create a codec under FE_TONEAREST; otherwise Unsupported. Cached derived
 *  values are computed only after admitting the FP environment. */
SEMQ_API semq_status_t semq_codec_create(const semq_config_t* cfg, semq_codec_t** out,
                                         semq_error_t* err);
SEMQ_API void semq_codec_free(semq_codec_t* codec);
SEMQ_API const semq_config_t* semq_codec_config(const semq_codec_t* codec);
/** Name of the kernel this codec runs ("scalar", "neon", "avx2", ...). */
SEMQ_API const char* semq_codec_backend(const semq_codec_t* codec);

/**
 * Encode `ids.n` rows of `cfg.dim` float32 values (contiguous, row-major).
 * Applies the input contract to every row: subnormal canonicalization,
 * finiteness, the unit-norm check, and the round-to-nearest requirement.
 * Row errors carry the input row index in `err->row` and the coordinate in
 * `err->field`. The result holds sorted ids, canonical rows, the manifest
 * and both digests.
 */
SEMQ_API semq_status_t semq_codec_encode(const semq_codec_t* codec, const semq_ids_t* ids,
                                         const float* vectors, const semq_pair_t* manifest,
                                         uint32_t n_pairs, semq_encoding_t** out,
                                         semq_error_t* err);

/** Representatives, `n × dim` float32, row `i` for `ids[i]`. Not normalized. */
SEMQ_API semq_status_t semq_codec_decode(const semq_codec_t* codec, const semq_encoding_t* enc,
                                         float* out, semq_error_t* err);
/** Symbols, `n × units_per_row` writable bytes, one per unit. */
SEMQ_API semq_status_t semq_codec_unpack(const semq_codec_t* codec, const semq_encoding_t* enc,
                                         uint8_t* out, semq_error_t* err);

/* -------------------------------------------------------------------------- */
/*  Encoding                                                                  */
/* -------------------------------------------------------------------------- */

/**
 * Build an Encoding from rows the caller already holds: `ids.n` rows of
 * `bytes_per_vector` bytes in input order, aligned with `ids`. Rows are
 * checked for canonicity (symbols in the alphabet, padding zero) and
 * copied once. `err->row` names the failing input row.
 */
SEMQ_API semq_status_t semq_encoding_create(const semq_config_t* cfg, const semq_ids_t* ids,
                                            const uint8_t* rows, const semq_pair_t* manifest,
                                            uint32_t n_pairs, semq_encoding_t** out,
                                            semq_error_t* err);
SEMQ_API void semq_encoding_free(semq_encoding_t* enc);

SEMQ_API const semq_config_t* semq_encoding_config(const semq_encoding_t* enc);
SEMQ_API uint32_t semq_encoding_id_kind(const semq_encoding_t* enc);
SEMQ_API uint64_t semq_encoding_len(const semq_encoding_t* enc);
/** Rows in id order, `n × bytes_per_vector` bytes. */
SEMQ_API const uint8_t* semq_encoding_rows(const semq_encoding_t* enc, uint64_t* len);
/** Sorted ids. `u64` kinds: `n` values. `utf8` kinds: `n + 1` offsets plus bytes. */
SEMQ_API const uint64_t* semq_encoding_ids_u64(const semq_encoding_t* enc);
SEMQ_API const uint64_t* semq_encoding_ids_utf8_offsets(const semq_encoding_t* enc);
SEMQ_API const uint8_t* semq_encoding_ids_utf8_bytes(const semq_encoding_t* enc, uint64_t* len);
/** Manifest pairs in canonical (bytewise key) order. */
SEMQ_API uint32_t semq_encoding_manifest_len(const semq_encoding_t* enc);
SEMQ_API semq_status_t semq_encoding_manifest_pair(const semq_encoding_t* enc, uint32_t i,
                                                   semq_pair_t* out, semq_error_t* err);
SEMQ_API void semq_encoding_content_digest(const semq_encoding_t* enc, uint8_t out[32]);
SEMQ_API void semq_encoding_state_id(const semq_encoding_t* enc, uint8_t out[32]);
/**
 * Row index of an id, or ::SEMQ_NONE when absent. ::SEMQ_ERR_INVALID_INPUT
 * when the id kind does not match the Encoding.
 */
SEMQ_API semq_status_t semq_encoding_find_u64(const semq_encoding_t* enc, uint64_t id,
                                              uint64_t* index, semq_error_t* err);
SEMQ_API semq_status_t semq_encoding_find_utf8(const semq_encoding_t* enc, const uint8_t* id,
                                               uint64_t len, uint64_t* index, semq_error_t* err);

/** One borrowed part of the file image. */
typedef struct {
    const uint8_t* ptr;
    uint64_t       len;
} semq_part_t;

/** Total size of the file image. */
SEMQ_API uint64_t semq_encoding_file_size(const semq_encoding_t* enc);
/** The file image as 5 ordered parts (header, ids, rows, manifest, footer)
 *  owned by the handle. `parts` has five writable entries; returned bytes
 *  remain borrowed until the Encoding's owner is released. Returns 5. */
SEMQ_API uint32_t semq_encoding_save_parts(const semq_encoding_t* enc, semq_part_t parts[5]);
/** Write the whole file image into `out` (`cap` ≥ file size). */
SEMQ_API semq_status_t semq_encoding_save(const semq_encoding_t* enc, uint8_t* out, uint64_t cap,
                                          semq_error_t* err);
/**
 * Parse a complete file image. Validates framing, config, sizes and limits,
 * ids, manifest, both digests and row canonicity, in that order, before
 * allocating anything proportional to a declared size. The bytes are copied.
 */
SEMQ_API semq_status_t semq_encoding_load(const uint8_t* buf, uint64_t len, semq_encoding_t** out,
                                          semq_error_t* err);
/**
 * Merge `k` Encodings with the same config, id kind and manifest and
 * pairwise-disjoint ids. ::SEMQ_ERR_INCOMPATIBLE on config or kind mismatch;
 * ::SEMQ_ERR_INVALID_INPUT on manifest mismatch (`field` = part index) or a
 * repeated id (`field` = part index, `row` = row within that part).
 */
SEMQ_API semq_status_t semq_encoding_concat(const semq_encoding_t* const* parts, uint32_t k,
                                            semq_encoding_t** out, semq_error_t* err);
/** `reference.diff(candidate)`. ::SEMQ_ERR_INCOMPATIBLE on config or kind mismatch. */
SEMQ_API semq_status_t semq_encoding_diff(const semq_encoding_t* reference,
                                          const semq_encoding_t* candidate, semq_diff_t** out,
                                          semq_error_t* err);

/* -------------------------------------------------------------------------- */
/*  Diff                                                                      */
/* -------------------------------------------------------------------------- */

/** Which id list of a Diff an accessor refers to. */
typedef enum {
    SEMQ_LIST_ADDED   = 0,
    SEMQ_LIST_REMOVED = 1,
    SEMQ_LIST_CHANGED = 2
} semq_list_t;

/** A change to one manifest key. `has_before`/`has_after` are 0 when the
 *  key is absent on that side; the pointers are then NULL. */
typedef struct {
    const uint8_t* key;
    uint32_t       key_len;
    int            has_before;
    const uint8_t* before;
    uint32_t       before_len;
    int            has_after;
    const uint8_t* after;
    uint32_t       after_len;
} semq_manifest_change_t;

/** The Diff keeps both Encodings alive until it is freed. */
SEMQ_API void semq_diff_free(semq_diff_t* diff);
SEMQ_API const semq_config_t* semq_diff_config(const semq_diff_t* diff);
SEMQ_API uint32_t semq_diff_id_kind(const semq_diff_t* diff);
SEMQ_API void semq_diff_reference_id(const semq_diff_t* diff, uint8_t out[32]);
SEMQ_API void semq_diff_candidate_id(const semq_diff_t* diff, uint8_t out[32]);
SEMQ_API uint64_t semq_diff_count(const semq_diff_t* diff, uint32_t list);
SEMQ_API uint64_t semq_diff_n_unchanged(const semq_diff_t* diff);
/** Id `i` of a list, in canonical order. For `u64` kinds `*u64` is set;
 *  for `utf8` kinds `*bytes`/`*len` are set. */
SEMQ_API semq_status_t semq_diff_id(const semq_diff_t* diff, uint32_t list, uint64_t i,
                                    uint64_t* u64, const uint8_t** bytes, uint64_t* len,
                                    semq_error_t* err);
/** Hamming distance of changed id `i` (units whose symbol differs). */
SEMQ_API semq_status_t semq_diff_hamming(const semq_diff_t* diff, uint64_t i, uint64_t* out,
                                         semq_error_t* err);
SEMQ_API uint32_t semq_diff_manifest_changes(const semq_diff_t* diff);
SEMQ_API semq_status_t semq_diff_manifest_change(const semq_diff_t* diff, uint32_t i,
                                                 semq_manifest_change_t* out, semq_error_t* err);
/**
 * Differing units of an id present on both sides. Writes up to `cap`
 * entries into `units`, `ref_symbols` and `cand_symbols` and the total count
 * into `*count` (0 for an identical row). ::SEMQ_ERR_INVALID_INPUT when the
 * id is absent from either side or its kind does not match. Pass `cap = 0`
 * to query the count. Each non-NULL output array covers `cap` elements;
 * any of the three arrays may be NULL to omit that output. count is required.
 */
SEMQ_API semq_status_t semq_diff_units_u64(const semq_diff_t* diff, uint64_t id, uint32_t* units,
                                           uint8_t* ref_symbols, uint8_t* cand_symbols,
                                           uint64_t cap, uint64_t* count, semq_error_t* err);
SEMQ_API semq_status_t semq_diff_units_utf8(const semq_diff_t* diff, const uint8_t* id,
                                            uint64_t len, uint32_t* units, uint8_t* ref_symbols,
                                            uint8_t* cand_symbols, uint64_t cap, uint64_t* count,
                                            semq_error_t* err);

/* -------------------------------------------------------------------------- */
/*  Floor                                                                     */
/* -------------------------------------------------------------------------- */

/** An envelope of observed variation, bound to the context it was measured
 *  in: the config, the id kind and the state_id of the reference every null
 *  diff was taken against. Immutable; may be shared between threads. */
typedef struct semq_floor semq_floor_t;

/** Create a floor from its fields. `config` MUST validate, `id_kind` MUST be
 *  0 or 1, `reference_id` is 32 bytes, `nulls ≥ 1`, `total_rows ≥ 1`,
 *  `changed_rows ≤ total_rows`, `hamming ≤ units_per_row(config)`;
 *  otherwise `InvalidInput` naming the field (a config field index, or
 *  SEMQ_NONE for the others). */
SEMQ_API semq_status_t semq_floor_create(const semq_config_t* config, uint32_t id_kind,
                                         const uint8_t reference_id[32], uint64_t nulls,
                                         uint64_t changed_rows, uint64_t total_rows,
                                         uint64_t hamming, semq_floor_t** out,
                                         semq_error_t* err);
SEMQ_API void semq_floor_free(semq_floor_t* floor);
/** Envelope of `k ≥ 1` null diffs; every input diff is within the result.
 *  All nulls MUST share config, id kind and reference state_id
 *  (`Incompatible` otherwise, `field` = the offending index) and each MUST
 *  be a valid null: rows in common, nothing added or removed, no change to
 *  `encoder` or `encoder_revision` (`InvalidInput`, `field` = index). The
 *  result records `nulls = k`. */
SEMQ_API semq_status_t semq_floor_measure(const semq_diff_t* const* nulls, uint32_t k,
                                          semq_floor_t** out, semq_error_t* err);
SEMQ_API const semq_config_t* semq_floor_config(const semq_floor_t* floor);
SEMQ_API uint32_t semq_floor_id_kind(const semq_floor_t* floor);
SEMQ_API void semq_floor_reference_id(const semq_floor_t* floor, uint8_t out[32]);
SEMQ_API uint64_t semq_floor_nulls(const semq_floor_t* floor);
SEMQ_API uint64_t semq_floor_changed_rows(const semq_floor_t* floor);
SEMQ_API uint64_t semq_floor_total_rows(const semq_floor_t* floor);
SEMQ_API uint64_t semq_floor_hamming(const semq_floor_t* floor);
/** `*out = 1` iff the diff is within the floor (exact integer arithmetic):
 *  rows in common, nothing removed, changed ratio and p99 hamming at most
 *  the floor's, and no change to `encoder` or `encoder_revision`. The
 *  floor MUST match the diff's config, id kind and reference state_id
 *  (`Incompatible` otherwise). */
SEMQ_API semq_status_t semq_diff_within(const semq_diff_t* diff, const semq_floor_t* floor,
                                        int* out, semq_error_t* err);

/* -------------------------------------------------------------------------- */
/*  Build information and utilities                                           */
/* -------------------------------------------------------------------------- */

/** Core version string, from the repository `VERSION` file. */
SEMQ_API const char* semq_core_version(void);
/** Compile-time build recipe identifier. Not a provenance proof. */
SEMQ_API const char* semq_build_id(void);
/** Effective kernel for an operator on this host ("scalar", "neon", ...). */
SEMQ_API const char* semq_backend_name(uint32_t op);
/** Static name of a status code ("ok", "invalid_input", ...). */
SEMQ_API const char* semq_status_name(uint32_t status);
/** SHA-256 (FIPS 180-4) of `len` bytes. Pinned by conformance vector 0. */
SEMQ_API void semq_sha256(const uint8_t* data, uint64_t len, uint8_t out[32]);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* SEMQ_H */