# facil.io — complete documentation (all versions)
# facil.io 0.8.x — complete documentation
# facil.io 0.8.x Documentation
The 0.8.x line re-organizes facil.io as the **fio-stl** library — a single-header-style C STL plus IO, HTTP, Pub/Sub and TLS 1.3 modules. Source code: [facil-io/cstl](https://github.com/facil-io/cstl).
**[Download fio-stl.h](https://github.com/facil-io/cstl/releases/download/0.8.0/fio-stl.h)** — the entire library as a single header file (release 0.8.0, one click).
> Stable release — the fio-stl library: single-header C core, IO reactor, HTTP, Pub/Sub and TLS 1.3.
The documentation is organized into module guides below, plus a complete [API Reference](https://facil.io/0.8.x/api/) generated from the headers. Press / or ⌘K anywhere to search.
## Core Library
Version macros, compiler shims, atomics, memory primitives, SIMD and math — the foundation everything builds on.
- [facil.io STL — Core](https://facil.io/0.8.x/facil-io-stl-core/)
- [Atomics and Locks](https://facil.io/0.8.x/atomics-and-locks/)
- [Bit Operations](https://facil.io/0.8.x/bit-operations/)
- [Compiler Attributes](https://facil.io/0.8.x/compiler-attributes/)
- [Constant-Time Helpers](https://facil.io/0.8.x/constant-time-helpers/)
- [Endian and Byte Ordering](https://facil.io/0.8.x/endian-and-byte-ordering/)
- [Linked Lists](https://facil.io/0.8.x/linked-lists/)
- [Logging and Assertions](https://facil.io/0.8.x/logging-and-assertions/)
- [Core Math Helpers](https://facil.io/0.8.x/core-math-helpers/)
- [Memory Alternatives and OS Patches](https://facil.io/0.8.x/memory-alternatives-and-os-patches/)
- [Memory Primitives](https://facil.io/0.8.x/memory-primitives/)
- [OS Detection](https://facil.io/0.8.x/os-detection/)
- [Core Random PRNG](https://facil.io/0.8.x/core-random-prng/)
- [Fake x86 SIMD](https://facil.io/0.8.x/fake-x86-simd/)
- [Static Scratch Allocator](https://facil.io/0.8.x/static-scratch-allocator/)
- [String and Buffer Information](https://facil.io/0.8.x/string-and-buffer-information/)
- [UTF-8 Helpers](https://facil.io/0.8.x/utf-8-helpers/)
- [Vector Math](https://facil.io/0.8.x/vector-math/)
- [Version and Settings](https://facil.io/0.8.x/version-and-settings/)
## Utilities & Parsers
Text conversion, hashing, glob, sorting, threads and the wire-format parsers (HTTP/1.x, JSON, RESP3, WebSocket…).
- [String / Number Conversion](https://facil.io/0.8.x/string-number-conversion/)
- [CRC32](https://facil.io/0.8.x/crc32/)
- [Glob Matching](https://facil.io/0.8.x/glob-matching/)
- [Index Mapped Array (iMap)](https://facil.io/0.8.x/index-mapped-array-imap/)
- [Multi-Precision Math](https://facil.io/0.8.x/multi-precision-math/)
- [Pseudo-Random and Hashing](https://facil.io/0.8.x/pseudo-random-and-hashing/)
- [Signal Monitoring](https://facil.io/0.8.x/signal-monitoring/)
- [Sorting](https://facil.io/0.8.x/sorting/)
- [Portable Threads](https://facil.io/0.8.x/portable-threads/)
- [URL / URI Parsing](https://facil.io/0.8.x/url-uri-parsing/)
- [ML Entity Decoding](https://facil.io/0.8.x/ml-entity-decoding/)
- [File Helpers](https://facil.io/0.8.x/file-helpers/)
- [HTTP/1.x Parser](https://facil.io/0.8.x/http-1-x-parser/)
- [JSON Parser](https://facil.io/0.8.x/json-parser/)
- [MIME Multipart Parser](https://facil.io/0.8.x/mime-multipart-parser/)
- [RESP3 Parser](https://facil.io/0.8.x/resp3-parser/)
- [Basic Socket Helpers](https://facil.io/0.8.x/basic-socket-helpers/)
- [State Callbacks](https://facil.io/0.8.x/state-callbacks/)
- [Time Helpers](https://facil.io/0.8.x/time-helpers/)
- [URL-Encoded Parser](https://facil.io/0.8.x/url-encoded-parser/)
- [WebSocket Parser](https://facil.io/0.8.x/websocket-parser/)
- [CLI Helpers](https://facil.io/0.8.x/cli-helpers/)
- [Memory Allocator](https://facil.io/0.8.x/memory-allocator/)
- [String Core](https://facil.io/0.8.x/string-core/)
- [GFM Markdown Parser](https://facil.io/0.8.x/gfm-markdown-parser/)
## System, Streams & Templates
Polling backends, task and timer queues, packet streams, Markdown and Mustache rendering.
- [POSIX Portable Polling API](https://facil.io/0.8.x/posix-portable-polling-api/)
- [POSIX Polling — epoll Backend](https://facil.io/0.8.x/posix-polling-epoll-backend/)
- [POSIX Polling — kqueue Backend](https://facil.io/0.8.x/posix-polling-kqueue-backend/)
- [POSIX Polling — poll Backend](https://facil.io/0.8.x/posix-polling-poll-backend/)
- [Task and Timer Queues](https://facil.io/0.8.x/task-and-timer-queues/)
- [Packet Data Stream](https://facil.io/0.8.x/packet-data-stream/)
- [Markdown to HTML bstr Renderer](https://facil.io/0.8.x/markdown-to-html-bstr-renderer/)
- [Mustache-ish Template Engine](https://facil.io/0.8.x/mustache-ish-template-engine/)
## Cryptography & TLS
Hashes, AEAD, signatures, X.509, KDFs, password hashing, compression and a standalone TLS 1.3 stack.
- [Crypto Core](https://facil.io/0.8.x/crypto-core/)
- [BLAKE2](https://facil.io/0.8.x/blake2/)
- [ChaCha20 & Poly1305](https://facil.io/0.8.x/chacha20-poly1305/)
- [HKDF](https://facil.io/0.8.x/hkdf/)
- [SHA-1](https://facil.io/0.8.x/sha-1/)
- [SHA-2](https://facil.io/0.8.x/sha-2/)
- [SHA-3](https://facil.io/0.8.x/sha-3/)
- [AES-GCM](https://facil.io/0.8.x/aes-gcm/)
- [Ed25519 & X25519](https://facil.io/0.8.x/ed25519-x25519/)
- [P-256 (secp256r1)](https://facil.io/0.8.x/p-256-secp256r1/)
- [P-384 (secp384r1)](https://facil.io/0.8.x/p-384-secp384r1/)
- [ASN.1 DER](https://facil.io/0.8.x/asn-1-der/)
- [RSA Signatures](https://facil.io/0.8.x/rsa-signatures/)
- [ML-KEM-768](https://facil.io/0.8.x/ml-kem-768/)
- [X.509 Certificates](https://facil.io/0.8.x/x-509-certificates/)
- [PEM Parser](https://facil.io/0.8.x/pem-parser/)
- [Argon2](https://facil.io/0.8.x/argon2/)
- [Lyra2](https://facil.io/0.8.x/lyra2/)
- [OTP (TOTP)](https://facil.io/0.8.x/otp-totp/)
- [Secrets](https://facil.io/0.8.x/secrets/)
- [Brotli](https://facil.io/0.8.x/brotli/)
- [DEFLATE / Gzip](https://facil.io/0.8.x/deflate-gzip/)
- [TLS 1.3 Standalone Library](https://facil.io/0.8.x/tls-1-3-standalone-library/)
## Dynamic Types
Dynamic strings, arrays, hash maps, reference counting and the FIOBJ soft dynamic types.
- [Dynamic Types — Module Group 200](https://facil.io/0.8.x/dynamic-types-module-group-200/)
- [Dynamic Strings — Module 201](https://facil.io/0.8.x/dynamic-strings-module-201/)
- [Dynamic Arrays — Module 202](https://facil.io/0.8.x/dynamic-arrays-module-202/)
- [Hash Maps and Sets — Module 210](https://facil.io/0.8.x/hash-maps-and-sets-module-210/)
- [Reference Counting Wrapper — Module 249](https://facil.io/0.8.x/reference-counting-wrapper-module-249/)
- [FIOBJ — Soft Dynamic Types — Module 250](https://facil.io/0.8.x/fiobj-soft-dynamic-types-module-250/)
## IO, HTTP & Pub/Sub
The IO reactor, TLS backends, Pub/Sub, Redis and the HTTP module that ties it all together.
- [IO and HTTP — Group Overview (400-range)](https://facil.io/0.8.x/io-and-http-group-overview-400-range/)
- [IO Reactor API (401 io api.h)](https://facil.io/0.8.x/io-reactor-api-401-io-api-h/)
- [IPC — Inter-Process Communication (404 ipc.h)](https://facil.io/0.8.x/ipc-inter-process-communication-404-ipc-h/)
- [OpenSSL TLS Backend](https://facil.io/0.8.x/openssl-tls-backend/)
- [Native TLS 1.3 IO Backend](https://facil.io/0.8.x/native-tls-1-3-io-backend/)
- [Pub/Sub — Publish/Subscribe Messaging (420 pubsub.h)](https://facil.io/0.8.x/pub-sub-publish-subscribe-messaging-420-pubsub-h/)
- [Redis — Pub/Sub Engine and Command Client (422 redis.h)](https://facil.io/0.8.x/redis-pub-sub-engine-and-command-client-422-redis-h/)
- [HTTP Module](https://facil.io/0.8.x/http-module/)
## API Reference
Every macro, type and function, per header file:
- [Core](https://facil.io/0.8.x/api/#grp-core)
- [Utilities](https://facil.io/0.8.x/api/#grp-util)
- [System](https://facil.io/0.8.x/api/#grp-system)
- [Cryptography](https://facil.io/0.8.x/api/#grp-crypto)
- [Dynamic Types](https://facil.io/0.8.x/api/#grp-types)
- [IO & HTTP](https://facil.io/0.8.x/api/#grp-io)
# fio-stl API Reference
The complete macro, type and function reference, one page per header file. Counts are listed per header.
## Core (000–1)
- [`000 copyright.h`](https://facil.io/0.8.x/api/000-copyright/) — 1 macro
- [`000 core.h`](https://facil.io/0.8.x/api/000-core/) — 311 macros, 14 types, 1439 functions
- [`001 header.h`](https://facil.io/0.8.x/api/001-header/) — 12 macros
- [`001 logging.h`](https://facil.io/0.8.x/api/001-logging/) — 1 macro
- [`001 memalt.h`](https://facil.io/0.8.x/api/001-memalt/) — 5 functions
- [`001 patches.h`](https://facil.io/0.8.x/api/001-patches/) — 2 functions
## Utilities (002–12)
- [`002 atol.h`](https://facil.io/0.8.x/api/002-atol/) — 4 macros, 1 type, 44 functions
- [`002 crc32.h`](https://facil.io/0.8.x/api/002-crc32/) — 1 function
- [`002 glob matching.h`](https://facil.io/0.8.x/api/002-glob-matching/) — 1 function
- [`002 imap.h`](https://facil.io/0.8.x/api/002-imap/) — 7 macros
- [`002 math.h`](https://facil.io/0.8.x/api/002-math/) — 6 functions
- [`002 random.h`](https://facil.io/0.8.x/api/002-random/) — 1 macro, 14 functions
- [`002 signals.h`](https://facil.io/0.8.x/api/002-signals/) — 3 macros, 1 type, 4 functions
- [`002 sort.h`](https://facil.io/0.8.x/api/002-sort/) — 3 macros
- [`002 threads.h`](https://facil.io/0.8.x/api/002-threads/) — 2 macros, 5 types, 26 functions
- [`002 url.h`](https://facil.io/0.8.x/api/002-url/) — 1 macro, 3 types, 3 functions
- [`003 entities.h`](https://facil.io/0.8.x/api/003-entities/) — 1 function
- [`004 files.h`](https://facil.io/0.8.x/api/004-files/) — 4 macros, 3 types, 21 functions
- [`004 http1 parser.h`](https://facil.io/0.8.x/api/004-http1-parser/) — 3 macros
- [`004 json.h`](https://facil.io/0.8.x/api/004-json/) — 2 macros, 2 types, 1 function
- [`004 multipart.h`](https://facil.io/0.8.x/api/004-multipart/) — 2 types, 1 function
- [`004 resp3.h`](https://facil.io/0.8.x/api/004-resp3/) — 19 macros, 4 types, 1 function
- [`004 sock.h`](https://facil.io/0.8.x/api/004-sock/) — 9 macros, 2 types, 24 functions
- [`004 state callbacks.h`](https://facil.io/0.8.x/api/004-state-callbacks/) — 1 type, 3 functions
- [`004 time.h`](https://facil.io/0.8.x/api/004-time/) — 17 functions
- [`004 urlencoded.h`](https://facil.io/0.8.x/api/004-urlencoded/) — 2 types, 1 function
- [`004 websocket parser.h`](https://facil.io/0.8.x/api/004-websocket-parser/) — 29 macros
- [`005 cli.h`](https://facil.io/0.8.x/api/005-cli/) — 7 macros, 1 type, 15 functions
- [`010 mem.h`](https://facil.io/0.8.x/api/010-mem/) — 27 macros, 23 functions
- [`011 string core.h`](https://facil.io/0.8.x/api/011-string-core/) — 18 macros, 3 types, 82 functions
- [`012 gfm.h`](https://facil.io/0.8.x/api/012-gfm/) — 10 macros
## System (102–104)
- [`102 poll api.h`](https://facil.io/0.8.x/api/102-poll-api/) — 7 macros, 2 types, 7 functions
- [`102 queue.h`](https://facil.io/0.8.x/api/102-queue/) — 3 macros, 4 types, 21 functions
- [`102 stream.h`](https://facil.io/0.8.x/api/102-stream/) — 3 macros, 2 types, 11 functions
- [`103 md2html.h`](https://facil.io/0.8.x/api/103-md2html/) — 2 macros
- [`104 mustache.h`](https://facil.io/0.8.x/api/104-mustache/) — 5 macros, 3 types, 6 functions
## Cryptography (150–190)
- [`150 crypto core.h`](https://facil.io/0.8.x/api/150-crypto-core/) — 2 types
- [`152 blake2.h`](https://facil.io/0.8.x/api/152-blake2/) — 2 types, 12 functions
- [`152 chacha20poly1305.h`](https://facil.io/0.8.x/api/152-chacha20poly1305/) — 7 functions
- [`152 sha1.h`](https://facil.io/0.8.x/api/152-sha1/) — 1 type, 3 functions
- [`152 sha2.h`](https://facil.io/0.8.x/api/152-sha2/) — 2 types, 12 functions
- [`152 sha2z hkdf.h`](https://facil.io/0.8.x/api/152-sha2z-hkdf/) — 2 macros, 3 functions
- [`152 sha3.h`](https://facil.io/0.8.x/api/152-sha3/) — 1 type, 16 functions
- [`153 aes.h`](https://facil.io/0.8.x/api/153-aes/) — 4 functions
- [`154 ed25519.h`](https://facil.io/0.8.x/api/154-ed25519/) — 2 macros, 11 functions
- [`154 p256.h`](https://facil.io/0.8.x/api/154-p256/) — 5 functions
- [`154 p384.h`](https://facil.io/0.8.x/api/154-p384/) — 2 functions
- [`155 der.h`](https://facil.io/0.8.x/api/155-der/) — 4 types, 14 functions
- [`155 rsa.h`](https://facil.io/0.8.x/api/155-rsa/) — 3 macros, 3 types, 3 functions
- [`156 mlkem.h`](https://facil.io/0.8.x/api/156-mlkem/) — 9 macros, 8 functions
- [`156 x509.h`](https://facil.io/0.8.x/api/156-x509/) — 34 macros, 10 types, 13 functions
- [`157 pem.h`](https://facil.io/0.8.x/api/157-pem/) — 3 types, 5 functions
- [`159 argon2.h`](https://facil.io/0.8.x/api/159-argon2/) — 2 types, 4 functions
- [`159 lyra2.h`](https://facil.io/0.8.x/api/159-lyra2/) — 1 type, 4 functions
- [`159 otp.h`](https://facil.io/0.8.x/api/159-otp/) — 1 type, 6 functions
- [`159 secret.h`](https://facil.io/0.8.x/api/159-secret/) — 5 functions
- [`162 brotli.h`](https://facil.io/0.8.x/api/162-brotli/) — 4 functions
- [`162 deflate.h`](https://facil.io/0.8.x/api/162-deflate/) — 1 type, 12 functions
- [`190 tls13.h`](https://facil.io/0.8.x/api/190-tls13/) — 16 macros, 16 types, 28 functions
## Dynamic Types (200–250)
- [`201 string.h`](https://facil.io/0.8.x/api/201-string/) — 7 macros, 1 type, 39 functions
- [`202 array.h`](https://facil.io/0.8.x/api/202-array/) — 9 macros, 2 types, 19 functions
- [`210 map.h`](https://facil.io/0.8.x/api/210-map/) — 3 macros, 4 types, 23 functions
- [`210 map2.h`](https://facil.io/0.8.x/api/210-map2/) — 3 macros
- [`249 reference counter.h`](https://facil.io/0.8.x/api/249-reference-counter/) — 17 functions
- [`250 fiobj.h`](https://facil.io/0.8.x/api/250-fiobj/) — 24 macros, 2 types, 40 functions
## IO & HTTP (400–439)
- [`401 io api.h`](https://facil.io/0.8.x/api/401-io-api/) — 6 macros, 13 types, 85 functions
- [`404 ipc.h`](https://facil.io/0.8.x/api/404-ipc/) — 13 macros, 4 types, 24 functions
- [`405 tls13.h`](https://facil.io/0.8.x/api/405-tls13/) — 1 function
- [`420 pubsub.h`](https://facil.io/0.8.x/api/420-pubsub/) — 2 macros, 4 types, 20 functions
- [`422 redis.h`](https://facil.io/0.8.x/api/422-redis/) — 2 macros, 2 types, 6 functions
- [`430 http api.h`](https://facil.io/0.8.x/api/430-http-api/) — 34 macros, 12 types, 107 functions
# facil.io STL — Core
## The C Server Toolbox Library
Welcome to the heart of the facil.io C STL. This is where the boring-but-essential stuff lives: version numbers, compiler incantations, OS patches, atomics, memory primitives, and the low-level helpers that everything else builds on. It is not glamorous, but without it the rest of the library would be a pile of undefined behavior and segfaults.
The core group covers the first two slices of the library — `000 core.h` and `001 header.h` — plus their specialized sidekicks. Each file below is a focused guide to one corner of that foundation.
## Core documentation index
- [001 version and settings](https://facil.io/0.8.x/version-and-settings/) — Version macros and the compile-time knobs that tune the library's behavior.
- [001 compiler attributes](https://facil.io/0.8.x/compiler-attributes/) — Compiler detection, C/C++ attribute helpers, and pointer-math macros.
- [001 os detection](https://facil.io/0.8.x/os-detection/) — POSIX/Windows detection, process helpers, and portable type patches.
- [001 atomics and locks](https://facil.io/0.8.x/atomics-and-locks/) — Atomic operations, spinlocks, atomic bitmap access, and thread-yield hints.
- [001 static alloc](https://facil.io/0.8.x/static-scratch-allocator/) — The round-robin scratch allocator for short-lived temporary buffers.
- [001 logging](https://facil.io/0.8.x/logging-and-assertions/) — Log levels, assertion macros, and the leak counter.
- [001 endian](https://facil.io/0.8.x/endian-and-byte-ordering/) — Byte-order detection, swaps, and conversions between host and network/little-endian layouts.
- [001 memory primitives](https://facil.io/0.8.x/memory-primitives/) — Low-level copy/move/compare helpers and unaligned buffer accessors.
- [001 memalt](https://facil.io/0.8.x/memory-alternatives-and-os-patches/) — Portable fallback memory routines and the environment helpers they lean on.
- [001 vector math](https://facil.io/0.8.x/vector-math/) — Wide integer unions from 128 to 4096 bits and their lane-wise arithmetic.
- [001 simd](https://facil.io/0.8.x/fake-x86-simd/) — Fake/portable x86 intrinsics and SIMD helpers built on the wide vector types.
- [001 linked lists](https://facil.io/0.8.x/linked-lists/) — Pointer-based and indexed linked-list macros.
- [001 constant time](https://facil.io/0.8.x/constant-time-helpers/) — Branchless selection, bitwise comparisons, and other security-sensitive primitives.
- [001 bit operations](https://facil.io/0.8.x/bit-operations/) — Rotations, popcount, bitmap access, and byte-value bit tricks.
- [001 math](https://facil.io/0.8.x/core-math-helpers/) — Hash primes, modular arithmetic, and multi-precision add/sub/mul helpers.
- [001 random core](https://facil.io/0.8.x/core-random-prng/) — The deterministic 128-bit PRNG and its reseeding/cycle-counter hooks.
- [001 string info](https://facil.io/0.8.x/string-and-buffer-information/) — Lightweight string and buffer descriptors plus equality helpers.
- [001 utf8](https://facil.io/0.8.x/utf-8-helpers/) — Encoding, decoding, and validation helpers for UTF-8 text.
Everything after the core builds on these pages. Pick a topic and dig in.
# Atomics and Locks
Concurrency primitives live in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/). The lock selector that chooses between spinlocks and OS mutexes is in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/).
## Atomic operations
All atomic macros use sequentially consistent ordering. They work on a pointer to the object and return the **previous** value, unless the name ends in `_fetch`, in which case they return the **new** value.
### Load and exchange
| Macro | Returns | Notes |
|---|---|---|
| `fio_atomic_load(dest, p_obj)` | assigns value to `dest` | Atomic read of `*p_obj`. |
| `fio_atomic_exchange(p_obj, value)` | previous value | Atomically sets `*p_obj = value`. |
| `fio_atomic_compare_exchange_p(p_obj, p_expected, p_desired)` | `1` on success, `0` on failure | System-specific compare-and-swap. `p_expected` may be overwritten with the current value. |
### Arithmetic and logic
| Macro | Operation | Returns |
|---|---|---|
| `fio_atomic_add(p_obj, value)` | `*p_obj += value` | previous value |
| `fio_atomic_sub(p_obj, value)` | `*p_obj -= value` | previous value |
| `fio_atomic_and(p_obj, value)` | `*p_obj &= value` | previous value |
| `fio_atomic_or(p_obj, value)` | `*p_obj \|= value` | previous value |
| `fio_atomic_xor(p_obj, value)` | `*p_obj ^= value` | previous value |
| `fio_atomic_add_fetch(p_obj, value)` | `*p_obj += value` | new value |
| `fio_atomic_sub_fetch(p_obj, value)` | `*p_obj -= value` | new value |
| `fio_atomic_and_fetch(p_obj, value)` | `*p_obj &= value` | new value |
| `fio_atomic_or_fetch(p_obj, value)` | `*p_obj \|= value` | new value |
| `fio_atomic_xor_fetch(p_obj, value)` | `*p_obj ^= value` | new value |
The implementation prefers GCC/Clang `__atomic` builtins, falls back to legacy `__sync` builtins, then C11 `stdatomic.h`, then MSVC intrinsics.
## Spinlocks
The default lock is an 8-bit spinlock with one main lock and seven sub-locks packed into the same byte.
| Type / macro | Meaning |
|---|---|
| `fio_lock_i` | `volatile unsigned char` spinlock type. |
| `FIO_LOCK_INIT` | Initializer (`0`). |
| `FIO_LOCK_SUBLOCK(i)` | Bit mask for sub-lock `i` (`0` to `7`). Combine with `\|`. |
### Single lock API
| Function | Behavior |
|---|---|
| `uint8_t fio_trylock(fio_lock_i *lock)` | Try to acquire the main lock. Returns `0` on success, `1` on failure. |
| `void fio_lock(fio_lock_i *lock)` | Busy-wait until the main lock is acquired. Avoid long waits. |
| `void fio_unlock(fio_lock_i *lock)` | Release the main lock. |
| `uint8_t fio_is_locked(fio_lock_i *lock)` | Returns non-zero if the main lock is held. |
### Group lock API
| Function | Behavior |
|---|---|
| `uint8_t fio_trylock_group(fio_lock_i *lock, uint8_t group)` | Try to acquire a group of sub-locks. Returns `0` on success, `1` on failure. |
| `void fio_lock_group(fio_lock_i *lock, uint8_t group)` | Busy-wait until the whole group is acquired. |
| `void fio_unlock_group(fio_lock_i *lock, uint8_t group)` | Release a group of sub-locks. |
| `uint8_t fio_is_group_locked(fio_lock_i *lock, uint8_t group)` | Returns non-zero if any sub-lock in `group` is held. |
| `uint8_t fio_trylock_full(fio_lock_i *lock)` | Try to lock all sub-locks at once. |
| `void fio_lock_full(fio_lock_i *lock)` | Busy-wait until every sub-lock is acquired. |
| `void fio_unlock_full(fio_lock_i *lock)` | Release every sub-lock. |
Example:
```c
fio_lock_i lock = FIO_LOCK_INIT;
if (!fio_trylock_group(&lock,
FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2))) {
/* critical section */
fio_unlock_group(&lock, FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2));
}
```
## Atomic bitmap access
Operate on a single bit inside a byte array. All four helpers are atomic at the byte level.
| Function | Behavior |
|---|---|
| `uint8_t fio_atomic_bit_get(void *map, size_t bit)` | Read bit `bit` from `map`. |
| `void fio_atomic_bit_set(void *map, size_t bit)` | Set bit `bit` to `1`. |
| `void fio_atomic_bit_unset(void *map, size_t bit)` | Set bit `bit` to `0`. |
| `void fio_atomic_bit_flip(void *map, size_t bit)` | Toggle bit `bit`. |
## Lock selector
[`./001 header.h`](https://facil.io/0.8.x/api/001-header/) chooses between facil.io spinlocks and OS mutexes. The default is spinlocks unless `FIO_USE_THREAD_MUTEX` is set to `1`.
| Macro | Purpose |
|---|---|
| `FIO_USE_THREAD_MUTEX_TMP` | Per-include override. Defaults to `FIO_USE_THREAD_MUTEX`. |
| `FIO_THREADS` | Defined when OS mutexes are selected. |
| `FIO___LOCK_NAME` | Human-readable name of the selected lock type. |
| `FIO___LOCK_TYPE` | The selected lock type (`fio_thread_mutex_t` or `fio_lock_i`). |
| `FIO___LOCK_INIT` | Initializer for the selected lock type. |
| `FIO___LOCK_DESTROY(lock)` | Destroy a lock instance. |
| `FIO___LOCK_LOCK(lock)` | Acquire the lock. |
| `FIO___LOCK_TRYLOCK(lock)` | Try to acquire the lock. |
| `FIO___LOCK_UNLOCK(lock)` | Release the lock. |
These macros are used by modules that need thread safety but do not care which lock type is active.
## Thread scheduling hints
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) and used by the spinlock back-off loops.
| Macro | Behavior |
|---|---|
| `FIO_THREAD_WAIT(nano_sec)` | Sleep the current thread for about `nano_sec` nanoseconds. POSIX uses `nanosleep`; Windows uses `Sleep` (rounded up to at least 1 ms). |
| `FIO_THREAD_YIELD()` | Hint to the CPU that the thread is spinning. Emits `pause` on x86, `yield` on ARM, calls `YieldProcessor()` on MSVC, or falls back to `sched_yield()` on POSIX. |
| `FIO_THREAD_RESCHEDULE()` | Short sleep via `FIO_THREAD_WAIT(4)`. Used by `fio_lock_group` to back off without fully suspending the thread. |
# Bit Operations
Bit-twiddling helpers defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
## Bit rotation
### Generic rotation macros
```c
#define FIO_LROT(i, bits)
#define FIO_RROT(i, bits)
```
Rotate the value `i` left or right by `bits`. The rotation count is masked to the width of `i`, so these macros work for any integer type.
### Typed rotation helpers
Each width has a left and right variant. When the compiler provides a rotate builtin it is used; otherwise a portable shift-or expression is emitted. All are marked `FIO_CONST`.
Note: the `fio_rrot8` builtin guard has a typo (`__has_builtin(__builtin_rotatrightt8)`), so the 8-bit right rotation always falls back to the portable implementation.
| Width | Left | Right |
|---|---|---|
| 8-bit | `fio_lrot8(i, bits)` | `fio_rrot8(i, bits)` |
| 16-bit | `fio_lrot16(i, bits)` | `fio_rrot16(i, bits)` |
| 32-bit | `fio_lrot32(i, bits)` | `fio_rrot32(i, bits)` |
| 64-bit | `fio_lrot64(i, bits)` | `fio_rrot64(i, bits)` |
| 128-bit | `fio_lrot128(i, bits)` | `fio_rrot128(i, bits)` |
The 128-bit variants are only available when the compiler supports `__int128`/`__uint128_t`.
### Endian-aware forward rotation
```c
fio_frot16(i, bits)
fio_frot32(i, bits)
fio_frot64(i, bits)
```
On little-endian systems `fio_frot*` maps to the right-rotation helpers; on big-endian systems it maps to the left-rotation helpers. These are macros, not functions.
## Combined rotation-XOR operations
Common SHA-2/BLAKE2-style combinations kept as separate operations so the compiler can optimize them as a unit.
```c
uint32_t fio_xor_rrot3_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c);
uint64_t fio_xor_rrot3_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c);
```
Computes `ROTR(x,a) ^ ROTR(x,b) ^ ROTR(x,c)`. Used by SHA-256/SHA-512 Sigma functions.
```c
uint32_t fio_xor_rrot2_shr_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c);
uint64_t fio_xor_rrot2_shr_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c);
```
Computes `ROTR(x,a) ^ ROTR(x,b) ^ (x >> c)`. Used by SHA-256/SHA-512 lowercase sigma functions for message scheduling.
## XOR masking
```c
void fio_xmask(void *buf, size_t len, uint64_t mask);
void fio_xmask_cpy(char *restrict dest,
const char *src,
size_t len,
uint64_t mask);
```
XOR every byte of a buffer with a repeating 64-bit `mask`. `fio_xmask` works in place; `fio_xmask_cpy` reads from `src` and writes to `dest`. If `dest == src`, `fio_xmask_cpy` behaves like `fio_xmask`.
Both functions process data in 64-bit chunks for speed and fall back to a byte-sized tail for the last `0`–`7` bytes. Performance is best when the buffer is aligned.
## Popcount and Hamming distance
```c
int fio_popcount(uint64_t n);
```
Returns the number of `1` bits in `n`. Uses `__builtin_popcountll` or MSVC `__popcnt64` when available; otherwise falls back to a portable parallel bit-count implementation.
```c
#define fio_hemming_dist(n1, n2) \
fio_popcount(((uint64_t)(n1) ^ (uint64_t)(n2)))
```
Returns the Hamming distance between `n1` and `n2` — the number of bits that differ. The macro is named `fio_hemming_dist` in the source (the conventional spelling is "Hamming").
## Bit isolation and indexing
```c
uint64_t fio_bits_lsb(uint64_t i);
uint64_t fio_bits_msb(uint64_t i);
```
* `fio_bits_lsb(i)` isolates the least-significant set bit.
* `fio_bits_msb(i)` isolates the most-significant set bit.
```c
size_t fio_bits_lsb_index(uint64_t i);
size_t fio_bits_msb_index(uint64_t i);
```
Return the bit index of the least-significant or most-significant set bit. Both return `(size_t)-1` when `i` is `0`.
```c
size_t fio_lsb_index_unsafe(uint64_t i);
size_t fio_msb_index_unsafe(uint64_t i);
```
Same as above but assume `i != 0`; calling them with `0` is undefined behavior. They use `__builtin_ctzll`/`__builtin_clzll` when available and fall back to `fio___single_bit_index_unsafe` — a `switch` over 64 power-of-two constants — otherwise.
## Byte detection in vectors
These helpers detect special byte values inside a packed integer. In the result, a matching byte lane is set to `0x80`; all other lanes are `0x00`.
### 32-bit vectors
```c
uint32_t fio_has_zero_byte32(uint32_t row);
uint32_t fio_has_byte32(uint32_t row, uint8_t byte);
uint32_t fio_has_full_byte32(uint32_t row);
```
* `fio_has_zero_byte32` detects `0x00` bytes.
* `fio_has_byte32` detects an arbitrary byte value.
* `fio_has_full_byte32` detects `0x00` bytes — the source's inline comment claims `0xFF`, but the implementation calls `fio_has_zero_byte32(row)`.
### 64-bit vectors
```c
uint64_t fio_has_zero_byte64(uint64_t row);
uint64_t fio_has_zero_byte_alt64(uint64_t row);
uint64_t fio_has_byte64(uint64_t row, uint8_t byte);
uint64_t fio_has_full_byte64(uint64_t row);
uint64_t fio_has_byte2bitmap(uint64_t result);
```
* `fio_has_zero_byte64` detects `0x00` bytes.
* `fio_has_zero_byte_alt64` is an alternate entry point with the same behavior as `fio_has_zero_byte64`; the source's warning about bitmap safety may be stale.
* `fio_has_byte64` detects an arbitrary byte value.
* `fio_has_full_byte64` detects `0xFF` bytes.
* `fio_has_byte2bitmap` packs a `fio_has_byte*` result into a single 8-bit bitmap.
## Bitmap access
Operate on a single bit inside a byte array. These are **not** atomic; use the [`fio_atomic_bit_*`](https://facil.io/0.8.x/atomics-and-locks/#atomic-bitmap-access) helpers for shared mutable bitmaps.
```c
uint8_t fio_bit_get(void *map, size_t bit);
void fio_bit_set(void *map, size_t bit);
void fio_bit_unset(void *map, size_t bit);
void fio_bit_flip(void *map, size_t bit);
```
* `fio_bit_get` returns `0` or `1`.
* `fio_bit_set` sets the bit to `1`.
* `fio_bit_unset` sets the bit to `0`.
* `fio_bit_flip` toggles the bit.
## See also
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth for all macros and functions above.
- [`./001 atomics and locks.md`](https://facil.io/0.8.x/atomics-and-locks/) — atomic bitmap helpers and locks.
- [`./001 constant time.md`](https://facil.io/0.8.x/constant-time-helpers/) — branchless bitwise primitives.
- [`./001 endian.md`](https://facil.io/0.8.x/endian-and-byte-ordering/) — byte ordering helpers.
# Compiler Attributes
The macros in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) and [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) paper over differences between C and C++, compilers, and inclusion models. This page is a quick reference for that portability scaffolding.
## C++ compatibility
Both core slices open an `extern "C"` block when compiled as C++. [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) also neutralizes C-only keywords inside that block:
- `register` is defined away (deprecated in C++).
- `restrict` is defined away (not valid in C++).
- `_Bool` is mapped to `bool`.
No action is required; including the headers gives C++ code a C-compatible surface.
## Keyword shims
Some compilers need a little help with C99/C11 keywords:
- **`inline`** — mapped to `__inline` when the compiler does not support the C99 keyword directly (mainly older MSVC).
- **`__thread`** — mapped to `__declspec(thread)` on MSVC and `_Thread_local` on C11 compilers; otherwise left as `__thread` when the compiler supports it.
## Compiler detection and no-op macros
When the compiler is not GCC or Clang, `__attribute__`, `__has_include`, `__has_builtin`, and `__has_attribute` are defined as no-ops so the rest of the code can use them unconditionally.
- **`FIO_NOOP`** — empty macro that adds whitespace. Useful to stop a function-like macro from being expanded.
- **`FIO_NOOP_FN(...)`** — empty variadic macro, handy as a default callback.
- **`FIO_NOOP_FN_NAME`** — expands to `(void)`.
## OS detection
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/); see [`./001 os detection.md`](https://facil.io/0.8.x/os-detection/) for full details.
- **`FIO_OS_POSIX`** — 1 on POSIX systems.
- **`FIO_OS_WIN`** — 1 on Windows.
- **`FIO_HAVE_UNIX_TOOLS`** — 0 (none), 1 (POSIX), 2 (MinGW), or 3 (Cygwin).
## Function attributes
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) and used throughout the library.
- **`FIO_MAYBE_UNUSED`** — `__attribute__((unused))` on GCC/Clang, empty otherwise.
- **`FIO_SFUNC`** — `static FIO_MAYBE_UNUSED`. Default internal linkage.
- **`FIO_IFUNC`** — `FIO_SFUNC inline`. Static inline helper.
- **`FIO_WARN_UNUSED`** — `__attribute__((warn_unused_result))` or empty.
- **`FIO_MIFN`** — `FIO_IFUNC FIO_WARN_UNUSED`. Inline math-style function that warns if the return value is ignored.
- **`FIO_CONST`** — function result depends only on arguments; no globals or side effects.
- **`FIO_PURE`** — no side effects, but may read global memory.
- **`FIO_WEAK`** — weak symbol. Empty on MinGW.
- **`DEPRECATED(reason)`** — `__attribute__((deprecated(reason)))`, with a fallback for GCC older than 4.5.
- **`FIO_CONSTRUCTOR(fname)`** — runs before `main` on GCC/Clang; on MSVC it places a pointer in `.CRT$XCU`.
- **`FIO_DESTRUCTOR(fname)`** — runs after `main` on GCC/Clang; on MSVC it is implemented as a constructor that registers `atexit(fname)`.
- **`FIO_LIKELY(cond)`** / **`FIO_UNLIKELY(cond)`** — branch-prediction hints. No-op on unknown compilers.
- **`FIO_PREFETCH(ptr)`** / **`FIO_PREFETCH_W(ptr)`** / **`FIO_PREFETCH_NT(ptr)`** — cache prefetch hints for read, write-exclusive, and non-temporal streaming access.
- **`FIO___PRINTF_STYLE(string_index, check_index)`** — tells the compiler to check `printf`-style format arguments. Empty on pure MSVC; uses the MinGW/Cygwin format attribute on those compilers; otherwise uses the GCC/Clang `printf` format attribute.
## Variable and type attributes
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
- **`FIO_ALIGN(bytes)`** — alignment hint. Uses `__attribute__((aligned(bytes)))` on GCC/Clang; currently empty on other compilers.
- **`FIO_WEAK_VAR`** — weak global variable. Uses `__declspec(selectany)` on Windows when selectany support is detected (MSVC or otherwise); `__attribute__((weak))` elsewhere.
## Macro stringifier
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
- **`FIO_MACRO2STR(macro)`** — expands `macro` and stringifies the result. Uses a two-step indirection so tokens like `__LINE__` are expanded first.
## Static assertions
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
- **`FIO_ASSERT_STATIC(cond, msg)`** — compile-time assertion. Uses C11 `_Static_assert` when available; otherwise falls back to a negative-array-size trick that produces a compile error on failure.
## Naming helpers
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
- **`FIO_NAME(prefix, postfix)`** → `prefix_postfix`.
- **`FIO_NAME2(prefix, postfix)`** → `prefix2postfix` (for conversion functions).
- **`FIO_NAME_BL(prefix, postfix)`** → `prefix_is_postfix` (for boolean tests).
- **`FIO_NAME_TEST(prefix, postfix)`** → internal test-function name (`fio___test_prefix_postfix`).
## Pointer math
All defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
- **`FIO_PTR_MATH_LMASK(T, ptr, bits)`** — keep the low `bits` of an address.
- **`FIO_PTR_MATH_RMASK(T, ptr, bits)`** — keep the high bits, zero the low `bits`.
- **`FIO_PTR_MATH_ADD(T, ptr, offset)`** — add `offset` bytes and cast to `T *`.
- **`FIO_PTR_MATH_SUB(T, ptr, offset)`** — subtract `offset` bytes and cast to `T *`.
- **`FIO_PTR_FIELD_OFFSET(T, field)`** — byte offset of `field` inside `T`. Uses `0xFF00` as a base address to keep AddressSanitizer happy.
- **`FIO_PTR_FROM_FIELD(T, field, ptr)`** — recover the containing `T` object from a pointer to one of its fields.
## Pointer tagging
Defined in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/); see [`./001 version and settings.md`](https://facil.io/0.8.x/version-and-settings/) for full details.
- **`FIO_PTR_TAG(p)`** / **`FIO_PTR_UNTAG(p)`** — default no-ops; override to embed tags in pointers.
- **`FIO_PTR_TAG_TYPE`** — when defined, pointer-returning functions use this type instead.
- **`FIO_PTR_TAG_VALIDATE(ptr)`** — validates a tagged pointer; default accepts non-NULL.
- **`FIO_PTR_TAG_VALID_OR_RETURN(tagged_ptr, value)`** — return `value` if validation fails.
- **`FIO_PTR_TAG_VALID_OR_RETURN_VOID(tagged_ptr)`** — return void if validation fails.
- **`FIO_PTR_TAG_VALID_OR_GOTO(tagged_ptr, label)`** — jump to `label` if validation fails.
- **`FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr)`** — cast `FIO_PTR_UNTAG(tagged_ptr)` to `untagged_type *`.
## Get/set helpers
Generate typed getters and setters for struct fields.
- **`FIO_DEF_GETSET_FUNC(static, namespace, T_type, F_type, F_name, on_set)`** — defines both `namespace_F_name` (getter) and `namespace_F_name_set` (setter).
- **`FIO_DEF_GETSET_FUNC_DEC(static, namespace, T_type, F_type, F_name)`** — declarations only.
- **`FIO_IFUNC_DEF_GETSET(namespace, T_type, F_type, F_name, on_set)`** — same, but uses `FIO_IFUNC` as the linkage. `FIO_IFUNC_DEF_GET` and `FIO_IFUNC_DEF_SET` exist for the individual pieces.
The setter returns the old value and calls `on_set(o)` after assignment.
## Recursive inclusion and linkage
facil.io headers are designed to be included more than once to generate code. [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) manages whether generated functions are `static` or `extern`.
- **`SFUNC_`** / **`IFUNC_`** — internal linkage selectors. On the first include they become `FIO_SFUNC`/`FIO_IFUNC` unless `FIO_EXTERN` is defined, in which case they are empty.
- **`SFUNC`** / **`IFUNC`** — public aliases for the current linkage selectors.
- **`FIO_EXTERN`** — define before including a module to emit non-static declarations and inline functions.
- **`FIO___RECURSIVE_INCLUDE`** — internal flag. Setting it to `99` before a recursive `#include` preserves the original `SFUNC`/`IFUNC` linkage instead of forcing it back to `static`.
## Compiler memory barriers
- **`FIO_COMPILER_GUARD`** — clobber memory and prevent the compiler from reordering instructions around it.
- **`FIO_COMPILER_GUARD_INSTRUCTION`** — prevent instruction reordering without the full memory clobber.
On GCC/Clang these are inline `asm volatile` barriers; on MSVC they map to `_ReadWriteBarrier` and `_WriteBarrier`.
## Thread scheduling
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/); see [`./001 atomics and locks.md`](https://facil.io/0.8.x/atomics-and-locks/) for full details.
- **`FIO_THREAD_WAIT(nano_sec)`** — sleep for about `nano_sec` nanoseconds (`Sleep` on Windows, `nanosleep` on POSIX).
- **`FIO_THREAD_YIELD()`** — yield the CPU (`pause`/`yield` instruction or `sched_yield`).
- **`FIO_THREAD_RESCHEDULE()`** — short sleep via `FIO_THREAD_WAIT(4)`.
## Address-sanitizer scaffolding
- **`FIO___ASAN_DETECTED`** — set when AddressSanitizer is active.
- **`FIO___ASAN_AVOID`** — expands to `no_sanitize_address` when ASan is detected; otherwise empty.
## Loop helpers
- **`FIO_FOR(i, count)`** — `for (size_t i = 0; i < (count); ++i)`.
- **`FIO_FOR_UNROLL(iterations, size_of_loop, i, action)`** — splits a loop into a small remainder and 256-byte chunks to nudge the compiler toward auto-vectorization. Used heavily by the memory primitives and vector-math templates. The SIMD file covers the portable x86 intrinsics that build on these helpers.
# Constant-Time Helpers
Branchless, timing-aware primitives defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
These helpers avoid data-dependent branches and early exits so that execution time does not leak information about the values being processed. They are used by the cryptographic modules, authentication code, and anywhere else a compiler might otherwise turn a secret into a branch.
## Boolean constants
```c
uintmax_t fio_ct_true(uintmax_t cond);
uintmax_t fio_ct_false(uintmax_t cond);
```
* `fio_ct_true(cond)` returns `1` if `cond` is non-zero, else `0`.
* `fio_ct_false(cond)` returns `1` if `cond` is zero, else `0`.
Both return only the value `0` or `1`, suitable for feeding into `fio_ct_if_bool`.
## Conditional selection
```c
uintmax_t fio_ct_if_bool(uintmax_t cond, uintmax_t a, uintmax_t b);
uintmax_t fio_ct_if(uintmax_t cond, uintmax_t a, uintmax_t b);
```
* `fio_ct_if_bool` returns `a` when `cond == 1`, otherwise `b`.
* `fio_ct_if` first normalizes `cond` with `fio_ct_true`, then returns `a` when `cond` is non-zero, otherwise `b`.
Both are branchless: they compute `b ^ (mask & (a ^ b))`.
## Min, max, and absolute value
```c
intmax_t fio_ct_max(intmax_t a, intmax_t b);
intmax_t fio_ct_min(intmax_t a, intmax_t b);
uintmax_t fio_ct_abs(intmax_t i);
```
* `fio_ct_max` / `fio_ct_min` perform signed comparisons without branches.
* `fio_ct_abs` returns the absolute value of `i`.
## ASCII case conversion
```c
char fio_ct_tolower(char c);
```
Returns the lowercase form of `c` when `c` is an ASCII uppercase letter (`A`–`Z`); otherwise returns `c` unchanged. Branchless and locale-independent.
## Bitwise mux, majority, and three-way XOR
```c
uint32_t fio_ct_mux32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_mux64(uint64_t x, uint64_t y, uint64_t z);
uint32_t fio_ct_maj32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_maj64(uint64_t x, uint64_t y, uint64_t z);
uint32_t fio_ct_xor3_32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_xor3_64(uint64_t x, uint64_t y, uint64_t z);
```
| Function | Operation | Formula |
|----------|-----------|---------|
| `fio_ct_mux32/64` | bitwise choose | `z ^ (x & (y ^ z))` |
| `fio_ct_maj32/64` | bitwise majority | `(x & y) \| (z & (x \| y))` |
| `fio_ct_xor3_32/64` | bitwise parity | `x ^ y ^ z` |
These are the SHA-style `Ch`, `Maj`, and parity functions, implemented without branches.
## Constant-time equality
```c
_Bool fio_ct_is_eq(const void *a, const void *b, size_t bytes);
```
Compares two memory regions and returns `1` if they are byte-for-byte identical, `0` otherwise. The comparison is timing-attack resistant: it always reads every byte and accumulates differences into a single flag before returning, regardless of where the first mismatch occurs.
## Secure zero and stack wipe
```c
void fio_secure_zero(void *a_, size_t bytes);
```
Zeros `bytes` starting at `a_`. The write is performed through a `volatile` pointer so the compiler cannot optimize it away. Use this for passwords, keys, and other sensitive material that must be erased from memory.
```c
#define FIO_MEM_STACK_WIPE(pages)
```
Allocates a volatile stack array of `(pages) * 4096` bytes and initializes it to zero. Useful for clearing sensitive stack scratch space; the `(void)stack_mem` use keeps the array alive through the scope.
# Endian and Byte Ordering
Macros and inline helpers for detecting and converting byte order. All are defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
facil.io assumes one of the two common orderings: big-endian or little-endian. Mixed-endian systems are not supported.
## Compile-Time Detection
The library normalizes two common compiler-provided symbols so downstream code can test endianness with simple `#if` checks.
- **`__BIG_ENDIAN__`** — set to `1` on big-endian systems, `0` on little-endian systems.
- **`__LITTLE_ENDIAN__`** — set to `1` on little-endian systems, `0` on big-endian systems.
The library also checks `__BYTE_ORDER__` as an input hint when the compiler provides it. If the compiler already defines `__BIG_ENDIAN__`, `__LITTLE_ENDIAN__`, or `__BYTE_ORDER__`, those values are honored. Otherwise facil.io defines `FIO_ENDIAN_ORDER_TEST` (`'1234'`), `FIO_LITTLE_ENDIAN_TEST` (`0x31323334UL`), and `FIO_BIG_ENDIAN_TEST` (`0x34333231UL`), then compares the unprefixed names in `#if ENDIAN_ORDER_TEST == LITTLE_ENDIAN_TEST` and `#elif ENDIAN_ORDER_TEST == BIG_ENDIAN_TEST`. If neither matches, compilation fails with an error.
The comparison deliberately uses the unprefixed names `ENDIAN_ORDER_TEST`, `LITTLE_ENDIAN_TEST`, and `BIG_ENDIAN_TEST`; those exact identifiers are not defined by facil.io. This mirrors the source.
These are constants, not variables; use them in `#if __BIG_ENDIAN__` rather than runtime `if` statements when possible.
## Runtime Detection
```c
unsigned int fio_is_little_endian(void);
unsigned int fio_is_big_endian(void);
```
Runtime tests that return a non-zero value when the machine matches the named ordering. `fio_is_big_endian` is implemented as `!fio_is_little_endian()`. Compilers usually constant-fold the result, but the functions are provided for cases where a runtime check is clearer.
## Byte Swapping
Reverse the byte order of a fixed-width integer. Each macro expands to the corresponding compiler builtin when available; otherwise it falls back to a portable bit-shifting implementation.
- `fio_bswap8(i)` — identity, defined so generic code can use the `8`-bit width without special cases.
- `fio_bswap16(i)` — swap bytes of a 16-bit integer.
- `fio_bswap32(i)` — swap bytes of a 32-bit integer.
- `fio_bswap64(i)` — swap bytes of a 64-bit integer.
- `fio_bswap128(i)` — swap bytes of a 128-bit integer. Only available when the compiler supports `__int128`/`__uint128_t`.
## Host ↔ Network Conversions
Network byte order is big-endian. These macros convert between the local byte order and network byte order.
- `fio_lton16(i)`, `fio_lton32(i)`, `fio_lton64(i)` — local to network.
- `fio_ntol16(i)`, `fio_ntol32(i)`, `fio_ntol64(i)` — network to local.
- `fio_lton128(i)` — local to network, defined only on little-endian systems where the compiler supports `__int128`/`__uint128_t`.
- `fio_ntol128(i)` — network to local, defined only when the compiler supports `__int128`/`__uint128_t`.
On big-endian systems the 16/32/64-bit macros are no-ops. On little-endian systems they byte-swap.
`fio_lton8(i)` and `fio_ntol8(i)` are also defined as identities so width-generic code can treat 8-bit values uniformly.
## Host ↔ Little-Endian Conversions
These helpers force values into little-endian byte order, which is also the ordering used by several internal data structures.
- `fio_ltole16(i)`, `fio_ltole32(i)`, `fio_ltole64(i)` — local to little-endian.
- `fio_ltole128(i)` — local to little-endian, only available when the compiler supports `__int128`/`__uint128_t`.
On little-endian systems they are no-ops. On big-endian systems they byte-swap.
`fio_ltole8(i)` is defined as an identity for generic width handling.
## Endian-Aware Shifts
A pair of shift macros whose direction depends on the host endianness. They are used in code that processes multi-byte values as bit patterns where the conceptual "forward" or "backward" direction reverses with endianness.
On little-endian systems:
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) << (bits))
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) >> (bits))
```
On big-endian systems they are defined only when the compiler supports `__int128`/`__uint128_t`:
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) >> (bits))
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) << (bits))
```
## See Also
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth for all macros and functions above.
- [`./000 core.md`](https://facil.io/0.8.x/facil-io-stl-core/) — overview of core types and helpers.
# Linked Lists
```c
#include "fio-stl.h"
```
Small, sharp linked-list macros from [`./000 core.h`](https://facil.io/0.8.x/api/000-core/). There are two families:
- pointer-based circular doubly linked lists, using `next` / `prev` pointers;
- indexed circular doubly linked lists, using integer offsets into an array.
The macros do not allocate memory, do not own stored objects, and do not check that nodes are initialized. Fast tools, pointy edges.
### Types
#### `fio_list_node_s`
```c
typedef struct fio_list_node_s {
struct fio_list_node_s *next;
struct fio_list_node_s *prev;
} fio_list_node_s;
```
The pointer-based list link. Embed it inside the struct you want to place in a list.
#### `FIO_LIST_NODE`
```c
#define FIO_LIST_NODE fio_list_node_s
```
Semantic alias for an embedded node field.
#### `FIO_LIST_HEAD`
```c
#define FIO_LIST_HEAD fio_list_node_s
```
Semantic alias for a list head/root. A list head is also a node, and an empty list points back to itself.
#### `fio_index32_node_s`
```c
typedef struct fio_index32_node_s {
uint32_t next;
uint32_t prev;
} fio_index32_node_s;
```
A 32-bit indexed-list link. It stores indexes into a caller-owned array.
#### `fio_index16_node_s`
```c
typedef struct fio_index16_node_s {
uint16_t next;
uint16_t prev;
} fio_index16_node_s;
```
A 16-bit indexed-list link.
#### `fio_index8_node_s`
```c
typedef struct fio_index8_node_s {
uint8_t next;
uint8_t prev;
} fio_index8_node_s;
```
An 8-bit indexed-list link.
#### `FIO_INDEXED_LIST32_NODE`, `FIO_INDEXED_LIST32_HEAD`
```c
#define FIO_INDEXED_LIST32_NODE fio_index32_node_s
#define FIO_INDEXED_LIST32_HEAD uint32_t
```
32-bit indexed-list node and head index types.
#### `FIO_INDEXED_LIST16_NODE`, `FIO_INDEXED_LIST16_HEAD`
```c
#define FIO_INDEXED_LIST16_NODE fio_index16_node_s
#define FIO_INDEXED_LIST16_HEAD uint16_t
```
16-bit indexed-list node and head index types.
#### `FIO_INDEXED_LIST8_NODE`, `FIO_INDEXED_LIST8_HEAD`
```c
#define FIO_INDEXED_LIST8_NODE fio_index8_node_s
#define FIO_INDEXED_LIST8_HEAD uint8_t
```
8-bit indexed-list node and head index types.
### API Functions
#### `FIO_LIST_INIT`
```c
#define FIO_LIST_INIT(obj) \
(fio_list_node_s) { .next = &(obj), .prev = &(obj) }
```
Initializer for a pointer-list node or head. The node points to itself.
Use it for heads:
```c
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
```
and for standalone nodes before insertion:
```c
item_s item = {.node = FIO_LIST_INIT(item.node)};
```
#### `FIO_LIST_EACH`
```c
#define FIO_LIST_EACH(type, node_name, head, pos) \
for (type *pos = FIO_PTR_FROM_FIELD(type, node_name, (head)->next), \
*next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, node_name, (head)->next->next); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.next)))
```
Iterates forward over every object after `head`. `pos` is declared by the macro as `type *`.
The next pointer is cached before the loop body, so removing the current `pos` is supported.
#### `FIO_LIST_EACH_REVERSED`
```c
#define FIO_LIST_EACH_REVERSED(type, node_name, head, pos) \
for (type *pos = FIO_PTR_FROM_FIELD(type, node_name, (head)->prev), \
*next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, node_name, (head)->next->prev); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.prev)))
```
Attempts to iterate backward over every object before `head`. `pos` is declared by the macro as `type *`.
**Note:** the current implementation caches the next pointer from `(head)->next->prev`, which equals `head` for a normal list, so the loop stops after visiting the first reversed node. Treat full reverse iteration as limited/buggy until the source macro is fixed.
#### `FIO_LIST_PUSH`
```c
#define FIO_LIST_PUSH(head, n) \
do { \
(n)->prev = (head)->prev; \
(n)->next = (head); \
(head)->prev->next = (n); \
(head)->prev = (n); \
} while (0)
```
Pushes node `n` to the end of the circular list, just before `head`.
Both `head` and `n` must point to initialized `FIO_LIST_NODE` / `FIO_LIST_HEAD` objects. The macro does not check whether `n` already belongs to another list.
#### `FIO_LIST_REMOVE`
```c
#define FIO_LIST_REMOVE(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
} while (0)
```
Removes `n` from its current list. The node's own `next` and `prev` fields are left pointing at the old neighbors.
#### `FIO_LIST_REMOVE_RESET`
```c
#define FIO_LIST_REMOVE_RESET(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
(n)->next = (n)->prev = (n); \
} while (0)
```
Removes `n`, then resets it to a self-linked node.
#### `FIO_LIST_POP`
```c
#define FIO_LIST_POP(type, node_name, dest_ptr, head) \
do { \
(dest_ptr) = FIO_PTR_FROM_FIELD(type, node_name, ((head)->next)); \
FIO_LIST_REMOVE(&(dest_ptr)->node_name); \
} while (0)
```
Removes the first object after `head` and stores the containing object pointer in `dest_ptr`.
Do not call this on an empty list. It will treat the head as a stored object and wander into undefined behavior.
#### `FIO_LIST_IS_EMPTY`
```c
#define FIO_LIST_IS_EMPTY(head) \
((!(head)) || ((!(head)->next) | ((head)->next == (head))))
```
Returns non-zero if `head` is `NULL`, `head->next` is `NULL`, or the head points to itself.
#### `FIO_INDEXED_LIST_PUSH`
```c
#define FIO_INDEXED_LIST_PUSH(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.prev = (root)[(head)].node_name.prev; \
(root)[n__].node_name.next = (head); \
(root)[(root)[(head)].node_name.prev].node_name.next = (n__); \
(root)[(head)].node_name.prev = (n__); \
} while (0)
```
Adds index `i` to the end of the indexed circular list, just before `head`. `head` is not changed.
`root` is the array containing all elements, and `node_name` is the embedded indexed-list node field.
#### `FIO_INDEXED_LIST_UNSHIFT`
```c
#define FIO_INDEXED_LIST_UNSHIFT(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.next = (root)[(head)].node_name.next; \
(root)[n__].node_name.prev = (head); \
(root)[(root)[(head)].node_name.next].node_name.prev = (n__); \
(root)[(head)].node_name.next = (n__); \
(head) = (n__); \
} while (0)
```
Adds index `i` to the beginning of the list and assigns `head = i`. The `head` argument must be an assignable lvalue.
#### `FIO_INDEXED_LIST_REMOVE`
```c
#define FIO_INDEXED_LIST_REMOVE(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
} while (0)
```
Removes index `i` from its current indexed list. The removed node keeps its previous links.
#### `FIO_INDEXED_LIST_REMOVE_RESET`
```c
#define FIO_INDEXED_LIST_REMOVE_RESET(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
(root)[n__].node_name.next = (root)[n__].node_name.prev = (n__); \
} while (0)
```
Removes index `i`, then resets its indexed node so `next == prev == i`.
#### `FIO_INDEXED_LIST_EACH`
```c
#define FIO_INDEXED_LIST_EACH(root, node_name, head, pos) \
for (size_t pos = (head), \
stooper___hd = (head), \
stopper___ils___ = 0, \
pos##___nxt = (root)[(head)].node_name.next; \
!stopper___ils___; \
(stopper___ils___ = ((pos = pos##___nxt) == stooper___hd)), \
pos##___nxt = (root)[pos].node_name.next)
```
Iterates forward over an indexed circular list, including `head`. `pos` is declared by the macro as `size_t`.
`head` must be a valid index. Empty-list sentinels are a caller convention, not something this macro handles.
#### `FIO_INDEXED_LIST_EACH_REVERSED`
```c
#define FIO_INDEXED_LIST_EACH_REVERSED(root, node_name, head, pos) \
for (size_t pos = ((root)[(head)].node_name.prev), \
pos##___nxt = \
((root)[((root)[(head)].node_name.prev)].node_name.prev), \
stooper___hd = (head), \
stopper___ils___ = 0; \
!stopper___ils___; \
((stopper___ils___ = (pos == stooper___hd)), \
(pos = pos##___nxt), \
(pos##___nxt = (root)[pos##___nxt].node_name.prev)))
```
Iterates backward over an indexed circular list, including the tail and eventually the head.
### Examples
#### Pointer list
```c
#include "fio-stl.h"
typedef struct item_s {
FIO_LIST_NODE node;
int value;
} item_s;
int main(void) {
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
item_s a = {.node = FIO_LIST_INIT(a.node), .value = 1};
item_s b = {.node = FIO_LIST_INIT(b.node), .value = 2};
FIO_LIST_PUSH(&list, &a.node);
FIO_LIST_PUSH(&list, &b.node);
int sum = 0;
FIO_LIST_EACH(item_s, node, &list, pos) {
sum += pos->value;
}
item_s *first = 0;
FIO_LIST_POP(item_s, node, first, &list);
return (sum == 3 && first == &a) ? 0 : 1;
}
```
#### Safe removal while iterating
```c
#include "fio-stl.h"
typedef struct item_s {
FIO_LIST_NODE node;
int value;
} item_s;
int main(void) {
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
item_s a = {.node = FIO_LIST_INIT(a.node), .value = 1};
item_s b = {.node = FIO_LIST_INIT(b.node), .value = 2};
FIO_LIST_PUSH(&list, &a.node);
FIO_LIST_PUSH(&list, &b.node);
FIO_LIST_EACH(item_s, node, &list, pos) {
if (pos->value == 1)
FIO_LIST_REMOVE_RESET(&pos->node);
}
return FIO_LIST_IS_EMPTY(&list) ? 1 : 0;
}
```
#### Indexed list
```c
#include "fio-stl.h"
typedef struct slot_s {
FIO_INDEXED_LIST16_NODE node;
int value;
} slot_s;
int main(void) {
slot_s slots[3] = {{{0}}};
FIO_INDEXED_LIST16_HEAD head = 0;
slots[0].node.next = slots[0].node.prev = 0;
slots[0].value = 10;
slots[1].node.next = slots[1].node.prev = 1;
slots[1].value = 20;
slots[2].node.next = slots[2].node.prev = 2;
slots[2].value = 30;
FIO_INDEXED_LIST_PUSH(slots, node, head, 1);
FIO_INDEXED_LIST_PUSH(slots, node, head, 2);
int sum = 0;
FIO_INDEXED_LIST_EACH(slots, node, head, pos) {
sum += slots[pos].value;
}
FIO_INDEXED_LIST_REMOVE_RESET(slots, node, 1);
return (sum == 60 && slots[1].node.next == 1) ? 0 : 1;
}
```
### Safety Notes
#### Initialization
Every head or node must be initialized before use. Pointer lists have `FIO_LIST_INIT`; indexed lists do not have a dedicated init macro, so initialize an indexed node with:
```c
root[i].node.next = root[i].node.prev = i;
```
#### Empty lists
Pointer lists have a natural empty state: `head->next == head`. `FIO_LIST_IS_EMPTY` checks it.
Indexed lists usually reserve an out-of-range value, often `(type)-1`, to mean empty. The indexed macros do not check for this sentinel; call them only when `head` is a valid index.
#### Index width
The indexed macros use `size_t` temporaries, then store into `uint32_t`, `uint16_t`, or `uint8_t` node fields. Keep every index representable by the chosen node type.
#### Removal and iteration
The iteration macros cache the next node/index before running the loop body, so removing the current non-head item is supported. If you remove or reset the current head in an indexed list, update your `head` value deliberately.
#### Memory ownership
Lists own no memory. Removing or popping unlinks nodes only; it does not destroy containing objects.
#### Thread safety
Mutating a list is not atomic. Multiple threads may read immutable lists, but concurrent mutation needs external synchronization.
#### Macro expansion
All APIs here are macros. Arguments may be evaluated more than once, and helper variable names are introduced inside loops. Pass simple lvalues/pointers, not expressions with side effects.
# Logging and Assertions
```c
#define FIO_LOG
#include "fio-stl.h"
```
Heap-allocation-free logging macros and assertion utilities. `FIO_LOG2STDERR` and the `FIO_LOG_*` macros are functional when `FIO_LOG` or `FIO_LEAK_COUNTER` is defined; otherwise they are no-ops.
`FIO_LOG` uses `libc` functions (`vsnprintf`, `fwrite`) and the library helper `fio_memcpy32`. If you are building without `libc`, provide your own implementations or shadowing macros before including the module.
## Log Levels
```c
#define FIO_LOG_LEVEL_NONE 0 /* no logging */
#define FIO_LOG_LEVEL_FATAL 1 /* fatal errors */
#define FIO_LOG_LEVEL_ERROR 2 /* errors and above */
#define FIO_LOG_LEVEL_WARNING 3 /* warnings and above */
#define FIO_LOG_LEVEL_INFO 4 /* info and above */
#define FIO_LOG_LEVEL_DEBUG 5 /* everything, including debug */
```
## Log Level Control
### `FIO_LOG_LEVEL_SET`
```c
#define FIO_LOG_LEVEL_SET(new_level) fio___log_level_set(new_level)
```
Sets the application-wide logging level. Returns the new level.
### `FIO_LOG_LEVEL_GET`
```c
#define FIO_LOG_LEVEL_GET() ((fio___log_level()))
```
Returns the current logging level as an integer.
```c
FIO_LOG_LEVEL_SET(FIO_LOG_LEVEL_WARNING);
int level = FIO_LOG_LEVEL_GET(); /* 3 */
```
## Configuration
### `FIO_LOG_LEVEL_DEFAULT`
Initial level. Defaults to `FIO_LOG_LEVEL_INFO` unless `DEBUG` is defined and truthy (`defined(DEBUG) && DEBUG`), in which case it is `FIO_LOG_LEVEL_DEBUG`.
### `FIO_STDERR_FILE`
```c
#define FIO_STDERR_FILE stderr
```
Output destination for all logging. Define it before including `./fio-stl.h` to redirect logs to another `FILE*`.
### `FIO_LOG_LENGTH_LIMIT`
```c
#define FIO_LOG_LENGTH_LIMIT 1024
```
Log buffer size. Keep this above `128`.
- When `FIO_LOG_LENGTH_LIMIT > 128`, the formatted message is limited to `FIO_LOG_LENGTH_LIMIT - 34` bytes, followed by a 32-byte truncation warning.
- When `FIO_LOG_LENGTH_LIMIT <= 128`, `vsnprintf` is capped at `FIO_LOG_LENGTH_LIMIT - 2` bytes; the truncation warning is appended afterward.
- Values `<= 2` are dangerous: the `vsnprintf` size argument underflows/overflows.
## Core Logging Function
### `FIO_LOG2STDERR`
```c
void FIO_LOG2STDERR(const char *format, ...);
```
`printf`-style output to `FIO_STDERR_FILE` using only stack-allocated memory. Truncation follows the two `FIO_LOG_LENGTH_LIMIT` paths described above. In `./fio-stl/001 logging.h` the name is `#undef`-ed before the `static` function definition, so a pre-include macro is removed and a post-inclusion function definition would conflict. Override it after inclusion only by defining `FIO_LOG2STDERR` as a macro.
## Log Macros
Most macros check the current log level before printing. `FIO_LOG_WRITE` has no level check and always prefixes output with the file and line number.
| Macro | Level | Prefix |
|-------|-------|--------|
| `FIO_LOG_FATAL(...)` | ≥ `FATAL` | `FATAL:` (bold inverse) |
| `FIO_LOG_ERROR(...)` | ≥ `ERROR` | `ERROR:` (bold) |
| `FIO_LOG_SECURITY(...)` | ≥ `ERROR` | `SECURITY:` (bold) |
| `FIO_LOG_WARNING(...)` | ≥ `WARNING` | `WARNING:` (dim) |
| `FIO_LOG_INFO(...)` | ≥ `INFO` | `INFO:` |
| `FIO_LOG_DEBUG(...)` | ≥ `DEBUG` | `DEBUG:` + file:line |
| `FIO_LOG_DEBUG2(...)` | ≥ `DEBUG` | `DEBUG:` |
| `FIO_LOG_WRITE(...)` | always | file:line |
## Debug-Only Log Macros
These expand to their non-`D` counterparts when `DEBUG` is defined, and to no-ops otherwise.
- `FIO_LOG_DDEBUG(...)`
- `FIO_LOG_DDEBUG2(...)`
- `FIO_LOG_DERROR(...)`
- `FIO_LOG_DSECURITY(...)`
- `FIO_LOG_DWARNING(...)`
- `FIO_LOG_DINFO(...)`
## Assertions
### `FIO_ASSERT`
```c
#define FIO_ASSERT(cond, ...)
```
If `cond` is false, prints a fatal message, prints `errno` and its string, sends `SIGINT` in debug builds, and calls `exit(-1)`.
### `FIO_ASSERT_ALLOC`
```c
#define FIO_ASSERT_ALLOC(ptr) FIO_ASSERT((ptr), "memory allocation failed.")
```
Convenience wrapper for allocation failures.
### `FIO_ASSERT_DEBUG`
```c
#define FIO_ASSERT_DEBUG(cond, ...)
```
Active only when `DEBUG` is defined. On failure it behaves like `FIO_ASSERT`, printing the file and line number before terminating.
### `FIO_ASSERT_STATIC`
```c
#define FIO_ASSERT_STATIC(cond, msg)
```
Compile-time assertion. `cond` must be a constant expression; `msg` is a string literal with no format specifiers. Falls back to a sized-array trick on pre-C11 compilers.
See also: [Compiler Attributes](./001 compiler attributes.md).
## Leak Counter Helpers
Enabled by `FIO_LEAK_COUNTER` (default `1`). `FIO_NO_LOG` and `FIO_LEAK_COUNTER` are mutually exclusive because leak reports are printed through the log.
### `FIO_LEAK_COUNTER_DEF`
```c
#define FIO_LEAK_COUNTER_DEF(name)
```
Defines a named counter and a cleanup function that reports remaining allocations at process exit.
### `FIO_LEAK_COUNTER_ON_ALLOC`
```c
#define FIO_LEAK_COUNTER_ON_ALLOC(name)
```
Increment the named counter. Call after a successful allocation.
### `FIO_LEAK_COUNTER_ON_FREE`
```c
#define FIO_LEAK_COUNTER_ON_FREE(name)
```
Decrement the named counter. Call when memory is freed. Detects double-free.
### `FIO_LEAK_COUNTER_COUNT`
```c
#define FIO_LEAK_COUNTER_COUNT(name)
```
Returns the current value of the named counter.
### `FIO_LEAK_COUNTER_SKIP_EXIT`
```c
#define FIO_LEAK_COUNTER_SKIP_EXIT 0
```
Set to `1` to prevent automatic leak reporting at exit.
## Example
```c
#define FIO_LOG
#include "fio-stl.h"
int main(void) {
FIO_LOG_LEVEL_SET(FIO_LOG_LEVEL_WARNING);
FIO_LOG_INFO("not printed");
FIO_LOG_WARNING("number invalid: %d", 42);
FIO_LOG2STDERR("direct message");
void *ptr = malloc(100);
FIO_ASSERT_ALLOC(ptr);
FIO_ASSERT(ptr != NULL, "expected a pointer");
free(ptr);
return 0;
}
```
# Core Math Helpers
Low-level integer arithmetic, prime testing, and multi-precision primitives defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
These functions work on plain `uint64_t` arrays. The higher-level division, shift, and inversion helpers live in [`./002 math.h`](https://facil.io/0.8.x/api/002-math/), and the wide-union wrappers are documented in [`./001 vector math.md`](https://facil.io/0.8.x/vector-math/).
## Hash primes
Four families of pre-selected prime constants, indexed `0` through `31`:
```c
FIO_U8_HASH_PRIME0 ... FIO_U8_HASH_PRIME31
FIO_U16_HASH_PRIME0 ... FIO_U16_HASH_PRIME31
FIO_U32_HASH_PRIME0 ... FIO_U32_HASH_PRIME31
FIO_U64_HASH_PRIME0 ... FIO_U64_HASH_PRIME31
```
Each fits in the named unsigned type and has about half of its bits set (`3`–`5` bits for the 8-bit primes). They are useful as hash mixers and for picking small prime bases.
## Modular arithmetic
```c
uint64_t fio_math_mod_mul64(uint64_t a, uint64_t b, uint64_t mod);
```
Computes `(a * b) % mod` without overflowing 64 bits. Uses `__uint128_t` when available; otherwise falls back to a double-and-add loop.
```c
uint64_t fio_math_mod_expo64(uint64_t base, uint64_t exp, uint64_t mod);
```
Right-to-left binary modular exponentiation. Calls `fio_math_mod_mul64` for each set bit of `exp`.
## Primality tests
```c
bool fio_math_is_uprime(uint64_t n);
bool fio_math_is_iprime(int64_t n);
```
`fio_math_is_uprime` tests whether `n` is probably prime. `fio_math_is_iprime` tests the absolute value of `n`.
For `n < FIO_PRIME_TABLE_LIMIT` (default `1024`) the test is deterministic using a small sieve. Larger values use Miller–Rabin with `10` rounds. Internal helpers `fio___is_prime_table` and `fio___is_prime_maybe` implement the two paths.
## 64-bit add / sub / mul with carry
```c
uint64_t fio_math_addc64(uint64_t a, uint64_t b,
uint64_t carry_in, uint64_t *carry_out);
uint64_t fio_math_subc64(uint64_t a, uint64_t b,
uint64_t borrow_in, uint64_t *borrow_out);
uint64_t fio_math_mulc64(uint64_t a, uint64_t b,
uint64_t *carry_out);
```
Return the low 64 bits of `a + b + carry_in`, `a - b - borrow_in`, or `a * b`; the outgoing carry/borrow/high word is written to `*carry_out`. They use compiler builtins where available and fall back to portable word arithmetic.
## Multi-precision add / sub / mul
All multi-precision routines treat arrays as little-endian `uint64_t` words: index `0` holds the least-significant word. Inputs are expected to have the same `len` (pad shorter inputs with zero).
```c
bool fio_math_add(uint64_t *dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
uint64_t fio_math_sub(uint64_t *dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
void fio_math_mul(uint64_t *restrict dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
```
* `fio_math_add` writes `a + b` to `dest` and returns the final carry.
* `fio_math_sub` writes `a - b` to `dest` and returns the final borrow.
* `fio_math_mul` writes the full `len * 2` word product of `a` and `b` to `dest`.
When `__uint128_t` is available, `fio_math_addc128` and `fio_math_add2` provide the same carry-style addition for 128-bit words.
## Internal multiplication helpers
`fio_math_mul` dispatches to specialized helpers depending on `len`:
* `fio___math_mul_long` — optimized schoolbook multiplication, loop-unrolled, with a `__uint128_t` fast path.
* `fio___math_mul_256` — fully unrolled 4-word (256-bit) multiply.
* `fio___math_mul_512` — semi-unrolled 8-word (512-bit) multiply.
* `fio___math_mul_1024` — semi-unrolled 16-word (1024-bit) multiply, mostly used as a fallback.
For large numbers:
```c
#define FIO___MATH_KARATSUBA_THRESHOLD 32
```
When `len` is at least this threshold and even, `fio_math_mul` calls `fio___math_mul_karatsuba`.
```c
void fio___math_mul_karatsuba(uint64_t *restrict dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
```
`fio___math_mul_karatsuba` splits `a` and `b` into low/high halves, computes `z0`, `z1`, and `z2`, then combines them as `z2*B^2 + z1*B + z0`. It uses `fio___math_mul_norecurse` for the three half-sized sub-products and allocates scratch buffers on the stack. `len` must be even; on MSVC or pre-C++14 the stack buffers are fixed at 256 words, so inputs are limited accordingly.
## See also
* [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth.
* [`./002 math.h`](https://facil.io/0.8.x/api/002-math/) — higher-level division, shift, and inversion helpers.
* [`./001 vector math.md`](https://facil.io/0.8.x/vector-math/) — wide-union `fio_uXXX` arithmetic built on these primitives.
# Memory Alternatives and OS Patches
Defines portable fallback memory routines and small OS compatibility patches. Implemented in [`./001 memalt.h`](https://facil.io/0.8.x/api/001-memalt/) and [`./001 patches.h`](https://facil.io/0.8.x/api/001-patches/); wired into the selector macros in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
## When `FIO_MEMALT` is used
Define `FIO_MEMALT` before including the STL to substitute standard-library memory and string calls with facil.io fallbacks:
```c
#define FIO_MEMALT
#include "fio-stl/include.h"
```
This is useful for freestanding or restricted environments, or when you want every memory call to go through the same portable implementation. The fallbacks are correct but usually slower than a good libc, so the default is to use compiler builtins or libc instead.
## Selector wiring
When `FIO_MEMALT` is defined, [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) routes any unset selector to the matching fallback:
| Selector | Fallback |
|----------|----------|
| `FIO_MEMCPY` | `fio_memcpy` |
| `FIO_MEMMOVE` | `fio_memcpy` |
| `FIO_MEMCMP` | `fio_memcmp` |
| `FIO_MEMCHR` | `fio_memchr` |
| `FIO_MEMSET` | `fio_memset` |
| `FIO_STRLEN` | `fio_strlen` |
If `FIO_MEMALT` is not defined, the selectors default to `__builtin_*` when available, otherwise to the standard `memcpy`, `memset`, `memchr`, `memcmp`, and `strlen`. You can also override any selector directly without enabling `FIO_MEMALT`.
## Fallback memory routines
All routines are declared in [`./001 memalt.h`](https://facil.io/0.8.x/api/001-memalt/).
### `fio_memcpy`
```c
void *fio_memcpy(void *dest, const void *src, size_t bytes);
```
Copies `bytes` from `src` to `dest`. Handles overlapping regions like `memmove`. Returns `dest` immediately if `dest == src`, `bytes` is zero, or either pointer is `NULL`.
For small, non-overlapping copies (≤64 bytes) it uses overlapping fixed-width copies. For larger or overlapping regions it uses a 64-byte buffered copy, choosing forward or backward direction as needed.
### `fio_memset`
```c
void *fio_memset(void *restrict dest, uint64_t data, size_t bytes);
```
Fills `bytes` at `dest` with the 8-byte pattern `data`. If `data` is below `0x100`, it is broadcast to every byte to match classic `memset` behavior. Returns `dest`, or `NULL` if `dest` is `NULL`.
### `fio_memchr`
```c
void *fio_memchr(const void *buffer, const char token, size_t len);
```
Finds the first byte equal to `token` in the first `len` bytes of `buffer`. Returns a pointer to the match, or `NULL` if not found, `buffer` is `NULL`, or `len` is zero.
Picks the best available implementation at compile time:
- AVX2 on x86-64 with intrinsics
- SSE2 on x86 with intrinsics
- NEON on ARM with intrinsics
- Scalar 128-bit block fallback otherwise
### `fio_memcmp`
```c
int fio_memcmp(const void *a, const void *b, size_t len);
```
Compares the first `len` bytes of `a` and `b`. Returns `1` if `a > b`, `-1` if `a < b`, and `0` if equal. Returns `0` immediately if `a == b` or `len` is zero.
Processes 64-byte blocks for large buffers, 8-byte blocks for medium buffers, and byte-by-byte for the tail.
### `fio_strlen`
```c
size_t fio_strlen(const char *str);
```
Returns the length of the NUL-terminated string `str`, or `0` if `str` is `NULL`. The implementation is a simple byte loop the compiler can auto-vectorize. Building with Address Sanitizer may flag speculative reads.
## OS patch environment helpers
[`./001 patches.h`](https://facil.io/0.8.x/api/001-patches/) provides `fio_sys_env` and `fio_sys_env_set` as portable replacements for `getenv` and `setenv`.
```c
char *fio_sys_env(const char *name);
int fio_sys_env_set(const char *name, const char *value, int overwrite);
```
- `fio_sys_env(name)` returns the value of the environment variable, or `NULL`.
- `fio_sys_env_set(name, value, overwrite)` sets the variable. If `overwrite` is zero and the variable already exists, it is left unchanged. Returns `0` on success, `-1` on failure.
On POSIX these are thin wrappers around `getenv`/`setenv`. On Windows they call `GetEnvironmentVariableA` and `SetEnvironmentVariableA` directly, bypassing macro redefinitions such as the one in Ruby's `win32.h` and avoiding `wchar_t` conversion issues.
## Other OS patches
[`./001 patches.h`](https://facil.io/0.8.x/api/001-patches/) also fills small portability gaps:
- **macOS < 10.12**: defines a `clock_gettime` fallback using `gettimeofday`, plus `CLOCK_REALTIME` and `CLOCK_MONOTONIC` placeholders.
- **Windows startup**: enables virtual terminal processing for stdout/stderr and sets binary file mode by default.
- **Windows symbol gaps**: supplies inline patches for `strcasecmp`, `write`, `read`, `clock_gettime`, and `pwrite`; maps `O_*`/`S_*` constants, `fstat`/`stat`/`lseek`/`unlink`, and defines `PATH_MAX`.
- **POSIX branch**: present but currently has no extra patches beyond the environment helpers.
# Memory Primitives
Low-level memory helpers used everywhere else in the STL. Most are defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/); the allocator abstraction macros live in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/).
## Memory-Function Selector Macros
These macros route standard-library-style calls to either compiler builtins or libc functions. Define any of them before inclusion to override the default.
| Macro | Default | Use |
|-------|---------|-----|
| `FIO_MEMCPY(dest, src, n)` | `__builtin_memcpy` if available, else `memcpy` | Variable-length copy. |
| `FIO_MEMMOVE(dest, src, n)` | `__builtin_memmove` if available, else `memmove` | Overlap-safe copy. |
| `FIO_MEMSET(dest, c, n)` | `__builtin_memset` if available, else `memset` | Fill memory. |
| `FIO_MEMCHR(ptr, c, n)` | `__builtin_memchr` if available, else `memchr` | Find byte. |
| `FIO_STRLEN(str)` | `__builtin_strlen` if available, else `strlen` | C string length. |
| `FIO_MEMCMP(a, b, n)` | `__builtin_memcmp` if available, else `memcmp` | Compare memory. |
If `FIO_MEMALT` is defined, any selector that was not already set is routed to the matching facil.io fallback:
```c
#define FIO_MEMCPY fio_memcpy
#define FIO_MEMMOVE fio_memcpy
#define FIO_MEMCMP fio_memcmp
#define FIO_MEMCHR fio_memchr
#define FIO_MEMSET fio_memset
#define FIO_STRLEN fio_strlen
```
Those fallback routines are documented in `./001 memalt.h` (the memalt module).
## Fixed-Size Copy Helpers
These functions copy an exact number of bytes from `src` to `dest`. They are implemented with the fixed-size `FIO_MEMCPY` path when builtins are available, otherwise with a compact byte loop.
- When `__builtin_memcpy` is available, each helper returns `dest` (the `__builtin_memcpy` return value).
- In the non-builtin fallback, each helper returns `dest + n`.
`fio_memcpy0` always returns `dest`.
```c
void *fio_memcpy0 (void *restrict dest, const void *restrict src);
void *fio_memcpy1 (void *restrict dest, const void *restrict src);
void *fio_memcpy2 (void *restrict dest, const void *restrict src);
void *fio_memcpy3 (void *restrict dest, const void *restrict src);
void *fio_memcpy4 (void *restrict dest, const void *restrict src);
void *fio_memcpy5 (void *restrict dest, const void *restrict src);
void *fio_memcpy6 (void *restrict dest, const void *restrict src);
void *fio_memcpy7 (void *restrict dest, const void *restrict src);
void *fio_memcpy8 (void *restrict dest, const void *restrict src);
void *fio_memcpy16(void *restrict dest, const void *restrict src);
void *fio_memcpy32(void *restrict dest, const void *restrict src);
void *fio_memcpy64(void *restrict dest, const void *restrict src);
void *fio_memcpy128(void *restrict dest, const void *restrict src);
void *fio_memcpy256(void *restrict dest, const void *restrict src);
void *fio_memcpy512(void *restrict dest, const void *restrict src);
void *fio_memcpy1024(void *restrict dest, const void *restrict src);
void *fio_memcpy2048(void *restrict dest, const void *restrict src);
void *fio_memcpy4096(void *restrict dest, const void *restrict src);
```
### Tail helpers
`fio_memcpyNx` copies `len & N` bytes — useful for handling the tail end of a larger copy without explicit branches.
```c
void *fio_memcpy0x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy7x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy15x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy31x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy63x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy127x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy255x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy511x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy1023x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy2047x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy4095x(void *restrict dest, const void *restrict src, size_t len);
```
### Internal unsafe helpers
Two internal helpers back the tail functions. They assume the regions do not overlap and perform no validation.
```c
void *fio___memcpy_unsafe_63x(void *restrict dest, const void *restrict src, size_t len);
void *fio___memcpy_unsafe_x (void *restrict dest, const void *restrict src, size_t len);
```
- `fio___memcpy_unsafe_63x` is optimized for lengths up to 63 bytes.
- `fio___memcpy_unsafe_x` handles arbitrary lengths, copying 128-byte blocks (or 256-byte blocks when `FIO_LIMIT_INTRINSIC_BUFFER` is `0`) and finishing with a 64-byte tail.
These are `static`/`static inline` helpers; do not use them directly unless you are implementing a higher-level routine and have already checked the inputs.
## Unaligned Buffer Accessors
Read and write integers from byte buffers that may not be naturally aligned. The suffix controls byte order:
- `u` — native (unspecified) byte order.
- `_le` — little-endian.
- `_be` — big-endian / network byte order.
```c
uint8_t fio_buf2u8u (const void *buf);
uint8_t fio_buf2u8_le(const void *buf);
uint8_t fio_buf2u8_be(const void *buf);
void fio_u2buf8u (void *buf, uint8_t v);
void fio_u2buf8_le(void *buf, uint8_t v);
void fio_u2buf8_be(void *buf, uint8_t v);
uint16_t fio_buf2u16u (const void *buf);
uint16_t fio_buf2u16_le(const void *buf);
uint16_t fio_buf2u16_be(const void *buf);
void fio_u2buf16u (void *buf, uint16_t v);
void fio_u2buf16_le(void *buf, uint16_t v);
void fio_u2buf16_be(void *buf, uint16_t v);
uint32_t fio_buf2u24u (const void *buf);
uint32_t fio_buf2u24_le(const void *buf);
uint32_t fio_buf2u24_be(const void *buf);
void fio_u2buf24u (void *buf, uint32_t v);
void fio_u2buf24_le(void *buf, uint32_t v);
void fio_u2buf24_be(void *buf, uint32_t v);
uint32_t fio_buf2u32u (const void *buf);
uint32_t fio_buf2u32_le(const void *buf);
uint32_t fio_buf2u32_be(const void *buf);
void fio_u2buf32u (void *buf, uint32_t v);
void fio_u2buf32_le(void *buf, uint32_t v);
void fio_u2buf32_be(void *buf, uint32_t v);
uint64_t fio_buf2u64u (const void *buf);
uint64_t fio_buf2u64_le(const void *buf);
uint64_t fio_buf2u64_be(const void *buf);
void fio_u2buf64u (void *buf, uint64_t v);
void fio_u2buf64_le(void *buf, uint64_t v);
void fio_u2buf64_be(void *buf, uint64_t v);
size_t fio_buf2zu(const void *buf);
void fio_u2bufzu(void *buf, size_t v);
```
`fio_buf2u24u` / `fio_u2buf24u` and `fio_buf2zu` / `fio_u2bufzu` select the correct implementation at compile time based on host endianness and `size_t` width.
Example:
```c
uint8_t packet[8] = {0};
fio_u2buf32_be(packet + 2, 0x12345678);
uint32_t n = fio_buf2u32_be(packet + 2); // 0x12345678
```
## Memory Allocator Abstraction
The STL types use these macros instead of calling `malloc`/`free` directly, so a custom allocator can be plugged in by defining them before inclusion. They are reset and redefined in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) for each inclusion cycle.
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) realloc((ptr), (new_size))
#define FIO_MEM_FREE(ptr, size) free((ptr))
#define FIO_MEM_ALIGNMENT_SIZE sizeof(long double)
#define FIO_MEM_REALLOC_IS_SAFE 0
```
- `FIO_MEM_REALLOC` — reallocates `ptr` from `old_size` to `new_size`, copying at least `copy_len` bytes if the object moves. `NULL` `ptr` acts like `malloc`; `new_size == 0` acts like `free`.
- `FIO_MEM_FREE` — frees memory allocated with `FIO_MEM_REALLOC`.
- `FIO_MEM_ALIGNMENT_SIZE` — the alignment the allocator guarantees.
- `FIO_MEM_REALLOC_IS_SAFE` — non-zero when the allocator zero-initializes returned memory.
If the custom `fio_malloc` allocator has already been included (`H___FIO_MALLOC___H` is defined), the defaults are routed to it instead:
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) fio_realloc2((ptr), (new_size), (copy_len))
#define FIO_MEM_FREE(ptr, size) fio_free((ptr))
#define FIO_MEM_ALIGNMENT_SIZE fio_malloc_alignment()
#define FIO_MEM_REALLOC_IS_SAFE fio_realloc_is_safe()
```
Define `FIO_MEM_RESET` before inclusion to force these defaults to be re-evaluated. Define `FIO_MEMORY_DISABLE` or `FIO_MALLOC_TMP_USE_SYSTEM` to force the system allocator for an inclusion cycle.
### Recursive variants
The underscore-suffixed macros exist for use during recursive `#include` cycles:
```c
FIO_MEM_REALLOC_
FIO_MEM_FREE_
FIO_MEM_ALIGNMENT_SIZE_
FIO_MEM_REALLOC_IS_SAFE_
```
They normally mirror the non-underscore versions. When `FIO_MALLOC_TMP_USE_SYSTEM` is set they are forced to the system `realloc`/`free` for that cycle only, while the non-underscore macros keep their outer meaning.
## Unaligned Memory Access Knob
```c
#define FIO_UNALIGNED_MEMORY_ACCESS_ENABLED 1
```
Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/). It is auto-detected: if `FIO_UNALIGNED_ACCESS` is `1` (the default) and the target CPU is known to tolerate unaligned access — x86, x86_64, ARM64, or another architecture that defines `__ARM_FEATURE_UNALIGNED` — then `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED` becomes `1`. Otherwise it is `0`.
Set `FIO_UNALIGNED_ACCESS` to `0` before inclusion to disable the detection attempt, or define `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED` directly to override the result.
## See Also
- `./001 memalt.h` (the memalt module) — variable-length fallback memory routines (`fio_memcpy`, `fio_memset`, `fio_memcmp`, `fio_memchr`, `fio_strlen`).
- [`./001 endian.md`](https://facil.io/0.8.x/endian-and-byte-ordering/) — byte-order detection and swap helpers.
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth for copy helpers and the unaligned-access knob.
- [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) — source of truth for the allocator macros.
# OS Detection
[`./000 core.h`](https://facil.io/0.8.x/api/000-core/) detects the host OS and defines a few portable helpers so the rest of the library can use one code path per platform.
## Platform macros
| Macro | POSIX value | Windows value | Notes |
|---|---|---|---|
| `FIO_OS_POSIX` | `1` | `0` | Set on Linux, macOS, BSD, and other `__unix__`/`__linux__`/`__APPLE__` systems. |
| `FIO_OS_WIN` | `0` | `1` | Set on Windows, including MinGW, MSYS2, Cygwin, and Borland. |
The Windows check is evaluated first, because MinGW/MSYS2 define both `_WIN32` and `__unix__`. Treating the system as Windows avoids calling POSIX socket functions on Winsock `SOCKET` handles.
After detection, `FIO_OS_WIN` always wins over `FIO_OS_POSIX`:
```c
#if FIO_OS_WIN
#undef FIO_OS_POSIX
#define FIO_OS_POSIX 0
#endif
```
## Unix-tools level
`FIO_HAVE_UNIX_TOOLS` tells the library how Unix-like the environment is:
- `0` — no Unix tools (pure MSVC).
- `1` — full POSIX.
- `2` — MinGW.
- `3` — Cygwin.
It is used to decide whether functions such as `pipe()`, `fcntl()`, and POSIX signal APIs are available.
## Process helpers
| Macro | POSIX | Windows |
|---|---|---|
| `fio_getpid` | `getpid` | `_getpid` |
| `FIO_KILL_SELF()` | `kill(0, SIGINT)` | `GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)` |
`FIO_KILL_SELF` is guarded by `#ifndef`, so you can define your own version before including the header. The default implementation is used by the assertion/debug machinery to deliver a signal to the running process.
## Thread sleep and yield
| Macro | POSIX | Windows |
|---|---|---|
| `FIO_THREAD_WAIT(nano_sec)` | `nanosleep` | `Sleep` |
| `FIO_THREAD_YIELD()` | see below | see below |
| `FIO_THREAD_RESCHEDULE()` | `FIO_THREAD_WAIT(4)` | `FIO_THREAD_WAIT(4)` |
`FIO_THREAD_WAIT(nano_sec)` sleeps the current thread for the requested number of nanoseconds. On POSIX it builds a `struct timespec` and calls `nanosleep`. On Windows it converts the interval to milliseconds and calls `Sleep`; any non-zero request is rounded up to at least 1 ms.
`FIO_THREAD_YIELD()` hints the processor that the thread is in a spin loop. On x86/x86-64 with GCC or Clang it emits a `pause` instruction; on ARM/AArch64 with GCC or Clang it emits a `yield` instruction; with MSVC it calls `YieldProcessor()`; otherwise it falls back to `sched_yield()` on POSIX.
`FIO_THREAD_RESCHEDULE()` yields the thread by sleeping for 4 nanoseconds (`FIO_THREAD_WAIT(4)`). It is used by the lock code to back off busy waits without fully suspending the thread.
## Portable type patches
On pure MSVC `ssize_t` is missing, so the core header defines it from `SSIZE_T`:
```c
typedef SSIZE_T ssize_t;
```
`SSIZE_MAX` and `SSIZE_MIN` are also provided if the system headers do not:
```c
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t)((~(size_t)0) >> 1))
#endif
#ifndef SSIZE_MIN
#define SSIZE_MIN ((ssize_t)(~((~(size_t)0) >> 1)))
#endif
```
## `printf`-style format checking
The `FIO___PRINTF_STYLE(string_index, check_index)` macro is defined differently on each toolchain so the compiler can check `printf`-style arguments. MinGW/Cygwin use the MinGW format attribute, pure MSVC leaves it empty, and POSIX/GCC/Clang use the standard `printf` format attribute. It is documented in [`./001 compiler attributes.md`](https://facil.io/0.8.x/compiler-attributes/).
# Core Random PRNG
An optionally-deterministic 128-bit pseudo-random number generator defined by a macro. It is a low-level building block for hash functions, test harnesses, and other internals that need an optionally repeatable, fast stream of bits.
For the high-level random module — `fio_rand64`, secure bytes, hashing, etc. — see [`./002 random.h`](https://facil.io/0.8.x/api/002-random/).
The implementation is in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
## `fio_cycle_counter`
```c
uint64_t fio_cycle_counter(void);
```
Returns a hardware cycle counter when one is available, or a number otherwise.
In non-deterministic mode, the PRNG uses this for timing-based jitter during reseeding.
## `FIO_DEFINE_RANDOM128_FN`
```c
#define FIO_DEFINE_RANDOM128_FN(extern, name, reseed_log, seed_offset)
```
Defines a named PRNG instance and its API. The most common deterministic form looks like:
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0x1234)
```
Parameters:
- `extern` — linkage prefix, e.g. `static` or `FIO_SFUNC`.
- `name` — base name for all generated functions and internal state.
- `reseed_log` — automatic reseed interval, `2^reseed_log` calls. Set to `0` for a fully deterministic stream.
- `seed_offset` — arbitrary offset added to the default seed constants.
### Non-Deterministic Mode
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 5, 0x1234)
```
When `reseed_log` is non-zero (and less than `64`), the generator calls `name_reseed()` automatically every `2^reseed_log` calls, mixing in timing data and the cycle counter. This makes the stream non-deterministic, and improves entropy.
In this example, reseeding is set to `5`, resulting in reseeding every 32 calls (collecting new entropy every 4,096 bytes).
## Generated API
For a definition `FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0)` the following functions are generated:
### `my_rng128`
```c
fio_u128 my_rng128(void);
```
Returns 128 pseudo-random bits. The return type is [`fio_u128`](https://facil.io/0.8.x/vector-math/), so the result can also be read through `.u64[0]`, `.u64[1]`, `.u8`, etc.
### `my_rng64`
```c
uint64_t my_rng64(void);
```
Returns 64 pseudo-random bits. Each call consumes half of a 128-bit result; a new 128-bit value is generated only every other call.
### `my_rng_bytes`
```c
void my_rng_bytes(void *dest, size_t len);
```
Fills `dest` with `len` pseudo-random bytes. Does nothing if `dest` is `NULL` or `len` is zero.
### `my_rng_reset`
```c
void my_rng_reset(void);
```
Resets the generator to its initial seeded state.
### `my_rng_reseed`
```c
void my_rng_reseed(void);
```
Re-seeds the generator using `CLOCK_MONOTONIC`, address/bits of local state, and `fio_cycle_counter()`. Useful after `fork()` or when you want to de-correlate separate processes.
### `my_rng_on_fork`
```c
void my_rng_on_fork(void *is_null);
```
Calls `my_rng_reseed()`. Fork-handler helper that takes an ignored `void *` argument.
## Example
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0)
void roll_die(int n) {
my_rng_reset();
for (int i = 0; i < n; ++i) {
uint64_t r = my_rng64();
printf("roll %d: %llu\n", i, (unsigned long long)(r % 6 + 1));
}
}
```
Because `reseed_log` is `0`, the same seed always produces the same sequence.
## Determinism and fork safety
- Set `reseed_log = 0` for deterministic, repeatable output.
- Set `reseed_log` to a practical value such as `31` for a non-deterministic stream that reseeds automatically.
- After `fork()`, call `name_reseed()` or register a wrapper that calls `name_on_fork()` with `pthread_atfork()` to avoid duplicated sequences in child processes.
## Testing
Test results for this generator are recorded in [`./721 rand128 macro.md`](https://facil.io/0.8.x/pseudo-random-and-hashing/).
## See also
- [`./002 random.h`](https://facil.io/0.8.x/api/002-random/) — high-level random/hashing module.
- [`./001 vector math.md`](https://facil.io/0.8.x/vector-math/) — the `fio_u128` return type.
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth.
# Fake x86 SIMD
```c
#define FIO_FAKE_X86 /* or FIO_FAKE_X86_SHADOW */
#include "fio-stl.h"
```
Portable x86 SIMD intrinsics for code that wants the x86 spell book even when the CPU does not. The implementation lives in [`./001 fx86.h`](https://github.com/facil-io/cstl001%20fx86.h) and uses the core wide unions from [`./000 core.h`](https://facil.io/0.8.x/api/000-core/), especially `fio_u128` and `fio_u256`.
The module is compiled only when `FIO_FAKE_X86` or `FIO_FAKE_X86_SHADOW` is defined before including the STL.
### Configuration Macros
#### `FIO_FAKE_X86`
```c
#define FIO_FAKE_X86
```
Enables the namespaced fake-intrinsic API: `fio_fx86_*` and `fio_fx86_256_*`.
On real x86 with `FIO___HAS_X86_INTRIN`, supported functions pass through to native intrinsics when the matching feature macro exists (`__SSE2__`, `__AES__`, `__AVX2__`, and friends). Otherwise the fallback code copies through `fio_u128` / `fio_u256` and performs lane-wise C operations.
#### `FIO_FAKE_X86_SHADOW`
```c
#define FIO_FAKE_X86_SHADOW
```
Enables the same API as `FIO_FAKE_X86`, then shadows real intrinsic names with macros:
```c
#define __m128i fio_fx86_m128i
#define __m256i fio_fx86_m256i
#define _mm_xor_si128(a, b) fio_fx86_xor_si128((a), (b))
#define _mm256_xor_si256(a, b) fio_fx86_256_xor_si256((a), (b))
```
Shadow mode is meant for compiling x86 code paths on non-x86 machines. It also undefines ARM intrinsic guard macros such as `FIO___HAS_ARM_INTRIN`, `FIO___HAS_ARM_SHA3_INTRIN`, `__ARM_NEON`, and `__ARM_FEATURE_CRYPTO`, then defines the x86 feature guards used by downstream code (`FIO___HAS_X86_INTRIN`, `__SSE2__`, `__SSSE3__`, `__SSE4_1__`, `__AES__`, `__PCLMUL__`, `__SHA__`, `__SHA512__`, `__AVX2__`, etc.).
Shadowed names are macros. Keep arguments simple and avoid depending on compiler-specific intrinsic types outside the code path being shadowed.
#### `FIO___HAS_X86_INTRIN`
```c
#define FIO___HAS_X86_INTRIN 1
#include
```
Core detection flag from `000 core.h`. It is defined on x86_64 when intrinsics are allowed and `immintrin.h` is available.
Intrinsic detection is disabled when `DEBUG` or `NO_INTRIN` is defined.
#### `FIO___HAS_ARM_INTRIN`
```c
#define FIO___HAS_ARM_INTRIN 1
#include
#include
```
Core detection flag from `000 core.h`. It is defined when ARM crypto + NEON headers are available. `FIO_FAKE_X86_SHADOW` undefines it so fake x86 code paths can be selected instead of ARM paths.
#### `FIO_FOR_UNROLL_GROUP_SIZE`
```c
#define FIO_FOR_UNROLL_GROUP_SIZE ((size_t)256U)
```
The byte stride used by `FIO_FOR_UNROLL` for compiler-friendly batches.
Most CPUs should be able to hold at least 512 bytes in their registers. The default value assumes half that is available for the optimized loop, as the action within each loop will likely require some available registers.
#### `FIO_FOR_UNROLL`
```c
#define FIO_FOR_UNROLL(iterations, size_of_loop, i, action) \
do { \
size_t i = 0; \
const size_t fio___unroll_remainder__ = \
((iterations) & ((FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)) - 1)); \
if (fio___unroll_remainder__) \
for (; i < fio___unroll_remainder__; ++i) \
action; \
if (iterations) \
for (; !((iterations) + 1) || (i < (iterations));) \
for (size_t j__loop__ = 0; \
j__loop__ < (FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)); \
++j__loop__, ++i) \
action; \
} while (0)
```
Runs a remainder loop, then fixed 256-byte batches. The macro declares `size_t i` inside the expansion; `action` may use that name.
#### `_MM_HINT_T0`, `_MM_HINT_T1`, `_MM_HINT_T2`, `_MM_HINT_NTA`
```c
#define _MM_HINT_T0 3
#define _MM_HINT_T1 2
#define _MM_HINT_T2 1
#define _MM_HINT_NTA 0
```
Defined in fake mode when native x86 types are not present, so `fio_fx86_prefetch` and shadowed `_mm_prefetch` calls can compile.
### Types
#### `fio_u128`
```c
typedef union fio_u128 {
FIO___UXXX_UGRP_DEF(128);
#if defined(__SIZEOF_INT128__)
__uint128_t alignment_for_u128_[1];
#endif
} fio_u128 FIO_ALIGN(16);
```
The 128-bit storage union used by the fallback implementation. It exposes unsigned, signed, floating-point, and vector views (`u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, `x8`, `x16`, `x32`, `x64`, etc.).
#### `fio_u256`
```c
typedef union fio_u256 {
fio_u128 u128[2];
FIO___UXXX_UGRP_DEF(256);
#if defined(__SIZEOF_INT256__)
__uint256_t alignment_for_u256_[1];
#endif
} fio_u256 FIO_ALIGN(32);
```
The 256-bit storage union used by AVX2 and SHA-512 fallbacks. It includes all `fio_u128`-style views plus `u128[2]`.
#### `FIO_UXXX_X64_DEF`, `FIO_UXXX_X32_DEF`, `FIO_UXXX_X16_DEF`, `FIO_UXXX_X8_DEF`
```c
#define FIO_UXXX_X64_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X32_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X16_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X8_DEF(name, bits) /* platform-specific */
```
Core vector-member definition macros. On ARM they use NEON vector arrays. On GCC/Clang with `vector_size`, they use a single full-width vector. Otherwise they are plain C arrays. The fake x86 API itself uses `fio_u128` / `fio_u256` conversions instead of assuming a specific vector layout.
#### `fio_fx86_m128i`
```c
typedef __m128i fio_fx86_m128i;
/* or */
typedef fio_u128 fio_fx86_m128i;
```
The canonical 128-bit integer SIMD type for this module. It is native `__m128i` when available, otherwise `fio_u128`.
#### `fio_fx86_m256i`
```c
typedef __m256i fio_fx86_m256i;
/* or */
typedef fio_u256 fio_fx86_m256i;
```
The canonical 256-bit integer SIMD type for this module. It is native `__m256i` when available, otherwise `fio_u256`.
#### `FIO___FX86_HAS_NATIVE_M128I`, `FIO___FX86_HAS_NATIVE_M256I`, `FIO___FX86_HAS_NATIVE_TYPES`
```c
#define FIO___FX86_HAS_NATIVE_M128I 1 /* or 0 */
#define FIO___FX86_HAS_NATIVE_M256I 1 /* or 0 */
#define FIO___FX86_HAS_NATIVE_TYPES \
(FIO___FX86_HAS_NATIVE_M128I && FIO___FX86_HAS_NATIVE_M256I)
```
Internal feature flags used to choose native x86 types vs. `fio_uXXX` unions.
### API Functions
#### `fio___fx86_to_u128`
```c
FIO_IFUNC fio_u128 fio___fx86_to_u128(fio_fx86_m128i v);
```
Copies a `fio_fx86_m128i` into a `fio_u128` using `fio_memcpy16`. Use this when you need portable lane access and the type might be native `__m128i`.
#### `fio___fx86_from_u128`
```c
FIO_IFUNC fio_fx86_m128i fio___fx86_from_u128(fio_u128 u);
```
Copies a `fio_u128` into the canonical 128-bit fake x86 type.
#### `fio___fx86_to_u256`
```c
FIO_IFUNC fio_u256 fio___fx86_to_u256(fio_fx86_m256i v);
```
Copies a `fio_fx86_m256i` into a `fio_u256` using `fio_memcpy32`.
#### `fio___fx86_from_u256`
```c
FIO_IFUNC fio_fx86_m256i fio___fx86_from_u256(fio_u256 u);
```
Copies a `fio_u256` into the canonical 256-bit fake x86 type.
#### `fio_fx86_loadu_si128`, `fio_fx86_load_si128`, `fio_fx86_storeu_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_loadu_si128(const void *p);
FIO_IFUNC fio_fx86_m128i fio_fx86_load_si128(const void *p);
FIO_IFUNC void fio_fx86_storeu_si128(void *p, fio_fx86_m128i a);
```
SSE2-style 128-bit load/store helpers. The fallback copies 16 bytes. The aligned load name mirrors `_mm_load_si128`; the fallback does not check alignment.
#### `fio_fx86_add_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_add_epi32(fio_fx86_m128i a,
fio_fx86_m128i b);
```
Adds four unsigned 32-bit lanes with normal wraparound.
#### `fio_fx86_xor_si128`, `fio_fx86_or_si128`, `fio_fx86_and_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_xor_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_or_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_and_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
```
Bitwise operations over the whole 128-bit value.
#### `fio_fx86_slli_si128`, `fio_fx86_srli_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_si128(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_si128(fio_fx86_m128i a, int imm8);
```
Shifts the whole 128-bit register by bytes, filling with zero. Values greater than or equal to 16 return zero in the fallback.
#### `fio_fx86_slli_epi32`, `fio_fx86_srli_epi32`, `fio_fx86_slli_epi64`, `fio_fx86_srli_epi64`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_epi32(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_epi32(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_epi64(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_epi64(fio_fx86_m128i a, int imm8);
```
Per-lane logical shifts for 32-bit and 64-bit lanes. Fallback shifts at or past the lane width produce zero.
#### `fio_fx86_shuffle_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_shuffle_epi32(fio_fx86_m128i a, int imm8);
```
Selects each output 32-bit lane from `a` using two bits per lane from `imm8`.
#### `fio_fx86_cmpeq_epi8`, `fio_fx86_movemask_epi8`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_cmpeq_epi8(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC int fio_fx86_movemask_epi8(fio_fx86_m128i a);
```
Byte equality produces `0xFF` or `0x00` per byte. The movemask packs the high bit of each byte into a 16-bit integer result.
#### `fio_fx86_set_epi32`, `fio_fx86_set_epi64x`, `fio_fx86_set_epi8`, `fio_fx86_set1_epi8`, `fio_fx86_setzero_si128`, `fio_fx86_cvtsi32_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi32(int e3, int e2, int e1, int e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi64x(int64_t e1, int64_t e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi8(char e15, char e14, char e13,
char e12, char e11, char e10,
char e9, char e8, char e7, char e6,
char e5, char e4, char e3, char e2,
char e1, char e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set1_epi8(char a);
FIO_IFUNC fio_fx86_m128i fio_fx86_setzero_si128(void);
FIO_IFUNC fio_fx86_m128i fio_fx86_cvtsi32_si128(int a);
```
SSE2 constructors. The `set` functions follow x86 argument order: high lanes first, low lanes last.
#### `fio_fx86_shuffle_epi8`, `fio_fx86_alignr_epi8`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_shuffle_epi8(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_alignr_epi8(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
```
SSSE3 helpers. `shuffle_epi8` performs byte lookup, zeroing output bytes whose control byte has bit 7 set. `alignr_epi8` concatenates `b:a`, shifts right by `imm8` bytes, and returns 16 bytes.
#### `fio_fx86_blend_epi16`, `fio_fx86_extract_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_blend_epi16(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
FIO_IFUNC int fio_fx86_extract_epi32(fio_fx86_m128i a, int imm8);
```
SSE4.1 helpers. `blend_epi16` selects each 16-bit lane from `a` or `b` by `imm8` bits. `extract_epi32` returns lane `imm8 & 3`.
#### `fio_fx86_prefetch`
```c
FIO_IFUNC void fio_fx86_prefetch(const void *p, int hint);
```
Prefetches on native SSE2. It is a no-op in fake mode.
#### `fio_fx86_aesenc_si128`, `fio_fx86_aesenclast_si128`, `fio_fx86_aeskeygenassist_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_aesenc_si128(fio_fx86_m128i a,
fio_fx86_m128i roundkey);
FIO_IFUNC fio_fx86_m128i fio_fx86_aesenclast_si128(fio_fx86_m128i a,
fio_fx86_m128i roundkey);
FIO_IFUNC fio_fx86_m128i fio_fx86_aeskeygenassist_si128(fio_fx86_m128i a,
int imm8);
```
AES-NI helpers. The fallback implements AES round logic with an S-box table. It is correct for portability tests, but the table lookups are cache-dependent; do not treat fake AES-NI as constant-time production crypto.
#### `fio_fx86_clmulepi64_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_clmulepi64_si128(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
```
PCLMULQDQ carry-less multiplication. `imm8 & 1` selects the 64-bit lane from `a`; `(imm8 >> 4) & 1` selects the lane from `b`. The result is a 128-bit GF(2) product.
#### `fio_fx86_sha256rnds2_epu32`, `fio_fx86_sha256msg1_epu32`, `fio_fx86_sha256msg2_epu32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256rnds2_epu32(fio_fx86_m128i cdgh,
fio_fx86_m128i abef,
fio_fx86_m128i msg);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256msg1_epu32(fio_fx86_m128i msg0,
fio_fx86_m128i msg1);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256msg2_epu32(fio_fx86_m128i msg4,
fio_fx86_m128i msg1);
```
SHA-NI SHA-256 helpers. The round function expects `cdgh`, `abef`, and message-plus-constant layout matching Intel SHA extensions.
#### `fio_fx86_sha1rnds4_epu32`, `fio_fx86_sha1nexte_epu32`, `fio_fx86_sha1msg1_epu32`, `fio_fx86_sha1msg2_epu32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1rnds4_epu32(fio_fx86_m128i abcd,
fio_fx86_m128i e0,
int func);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1nexte_epu32(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1msg1_epu32(fio_fx86_m128i msg0,
fio_fx86_m128i msg1);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1msg2_epu32(fio_fx86_m128i msg5,
fio_fx86_m128i msg1);
```
SHA-NI SHA-1 helpers. `func & 3` selects Ch, Parity, Maj, or Parity for `sha1rnds4`.
#### `fio_fx86_256_loadu_si256`, `fio_fx86_256_load_si256`, `fio_fx86_256_storeu_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_loadu_si256(const void *p);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_load_si256(const void *p);
FIO_IFUNC void fio_fx86_256_storeu_si256(void *p, fio_fx86_m256i a);
```
AVX2-style 256-bit load/store helpers. The fallback copies 32 bytes.
#### `fio_fx86_256_add_epi16`, `fio_fx86_256_sub_epi16`, `fio_fx86_256_mullo_epi16`, `fio_fx86_256_add_epi32`, `fio_fx86_256_sub_epi32`, `fio_fx86_256_mullo_epi32`, `fio_fx86_256_add_epi64`, `fio_fx86_256_sub_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_mullo_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_mullo_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi64(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi64(fio_fx86_m256i a,
fio_fx86_m256i b);
```
AVX2 packed integer arithmetic. Operations wrap by lane width; `mullo` keeps the low half of each product.
#### `fio_fx86_256_slli_epi32`, `fio_fx86_256_srai_epi32`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_slli_epi32(fio_fx86_m256i a, int imm8);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_srai_epi32(fio_fx86_m256i a, int imm8);
```
Per-lane 32-bit shifts. `srai` is arithmetic right shift; fallback values at or past 32 bits become the sign bit extended value.
#### `fio_fx86_256_xor_si256`, `fio_fx86_256_and_si256`, `fio_fx86_256_or_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_xor_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_and_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_or_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
```
Bitwise operations over the whole 256-bit value.
#### `fio_fx86_256_cmpeq_epi8`, `fio_fx86_256_movemask_epi8`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_cmpeq_epi8(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC int fio_fx86_256_movemask_epi8(fio_fx86_m256i a);
```
AVX2 byte equality and 32-bit byte movemask.
#### `fio_fx86_256_set1_epi8`, `fio_fx86_256_set1_epi16`, `fio_fx86_256_set1_epi32`, `fio_fx86_256_set1_epi64x`, `fio_fx86_256_set_epi64x`, `fio_fx86_256_set_epi8`, `fio_fx86_256_setzero_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi8(char a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi16(short a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi32(int a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi64x(long long a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set_epi64x(long long e3,
long long e2,
long long e1,
long long e0);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set_epi8(char e31, char e30, char e29,
char e28, char e27, char e26,
char e25, char e24, char e23,
char e22, char e21, char e20,
char e19, char e18, char e17,
char e16, char e15, char e14,
char e13, char e12, char e11,
char e10, char e9, char e8,
char e7, char e6, char e5,
char e4, char e3, char e2,
char e1, char e0);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_setzero_si256(void);
```
AVX2 constructors. Like Intel intrinsics, the explicit `set` functions receive high lanes first and low lanes last.
#### `fio_fx86_256_shuffle_epi8`, `fio_fx86_256_alignr_epi8`, `fio_fx86_256_permute4x64_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_shuffle_epi8(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_alignr_epi8(fio_fx86_m256i a,
fio_fx86_m256i b,
int imm8);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_permute4x64_epi64(fio_fx86_m256i a,
int imm8);
```
AVX2 shuffle/permute helpers. `shuffle_epi8` and `alignr_epi8` operate inside each 128-bit lane. `permute4x64_epi64` selects among the four 64-bit lanes across the full 256-bit value.
#### `fio_fx86_256_packs_epi32`, `fio_fx86_256_cvtepi16_epi32`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_packs_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_cvtepi16_epi32(fio_fx86_m128i a);
```
AVX2 pack/convert helpers. `packs_epi32` saturates 32-bit signed lanes to 16-bit signed lanes, per 128-bit lane. `cvtepi16_epi32` sign-extends eight 16-bit values from the low 128 bits into eight 32-bit lanes.
#### `fio_fx86_256_castsi256_si128`, `fio_fx86_256_extracti128_si256`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_256_castsi256_si128(fio_fx86_m256i a);
FIO_IFUNC fio_fx86_m128i fio_fx86_256_extracti128_si256(fio_fx86_m256i a,
int imm8);
```
Extract 128-bit halves from a 256-bit value. `extracti128` uses `imm8 & 1`.
#### `fio_fx86_256_sha512rnds2_epi64`, `fio_fx86_256_sha512msg1_epi64`, `fio_fx86_256_sha512msg2_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512rnds2_epi64(fio_fx86_m256i state1,
fio_fx86_m256i state0,
fio_fx86_m256i wk);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512msg1_epi64(fio_fx86_m256i w0,
fio_fx86_m256i w1);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512msg2_epi64(fio_fx86_m256i w0,
fio_fx86_m256i w1);
```
Intel SHA-512 extension helpers using four 64-bit lanes.
### Shadowed Intrinsic Families
#### `_mm_*`
With `FIO_FAKE_X86_SHADOW`, these SSE/SHA/AES names are mapped to the matching `fio_fx86_*` functions:
```c
_mm_loadu_si128, _mm_load_si128, _mm_storeu_si128,
_mm_add_epi32,
_mm_xor_si128, _mm_or_si128, _mm_and_si128,
_mm_slli_si128, _mm_srli_si128,
_mm_slli_epi32, _mm_srli_epi32,
_mm_slli_epi64, _mm_srli_epi64,
_mm_shuffle_epi32, _mm_shuffle_epi8, _mm_alignr_epi8,
_mm_cmpeq_epi8, _mm_movemask_epi8,
_mm_set_epi32, _mm_set_epi64x, _mm_set_epi8,
_mm_set1_epi8, _mm_setzero_si128, _mm_cvtsi32_si128,
_mm_blend_epi16, _mm_extract_epi32, _mm_prefetch,
_mm_aesenc_si128, _mm_aesenclast_si128, _mm_aeskeygenassist_si128,
_mm_clmulepi64_si128,
_mm_sha256rnds2_epu32, _mm_sha256msg1_epu32, _mm_sha256msg2_epu32,
_mm_sha1rnds4_epu32, _mm_sha1nexte_epu32,
_mm_sha1msg1_epu32, _mm_sha1msg2_epu32
```
#### `_mm256_*`
With `FIO_FAKE_X86_SHADOW`, these AVX2/SHA-512 names are mapped to the matching `fio_fx86_256_*` functions:
```c
_mm256_loadu_si256, _mm256_load_si256, _mm256_storeu_si256,
_mm256_add_epi16, _mm256_sub_epi16, _mm256_mullo_epi16,
_mm256_add_epi32, _mm256_sub_epi32, _mm256_mullo_epi32,
_mm256_add_epi64, _mm256_sub_epi64,
_mm256_slli_epi32, _mm256_srai_epi32,
_mm256_xor_si256, _mm256_and_si256, _mm256_or_si256,
_mm256_cmpeq_epi8, _mm256_movemask_epi8,
_mm256_set1_epi8, _mm256_set1_epi16, _mm256_set1_epi32,
_mm256_set1_epi64x, _mm256_set_epi64x, _mm256_set_epi8,
_mm256_setzero_si256,
_mm256_shuffle_epi8, _mm256_alignr_epi8, _mm256_permute4x64_epi64,
_mm256_packs_epi32, _mm256_cvtepi16_epi32,
_mm256_castsi256_si128, _mm256_extracti128_si256,
_mm256_sha512rnds2_epi64, _mm256_sha512msg1_epi64,
_mm256_sha512msg2_epi64
```
### Examples
#### 128-bit add
```c
#define FIO_FAKE_X86
#include "fio-stl.h"
#include
int main(void) {
uint32_t a_in[4] = {1, 2, 3, 4};
uint32_t b_in[4] = {10, 20, 30, 40};
uint32_t out[4];
fio_fx86_m128i a = fio_fx86_loadu_si128(a_in);
fio_fx86_m128i b = fio_fx86_loadu_si128(b_in);
fio_fx86_m128i c = fio_fx86_add_epi32(a, b);
fio_fx86_storeu_si128(out, c);
return (out[0] == 11 && out[3] == 44) ? 0 : 1;
}
```
#### AVX2-style byte equality
```c
#define FIO_FAKE_X86
#include "fio-stl.h"
#include
int main(void) {
uint8_t a[32] = {0};
uint8_t b[32] = {0};
b[7] = 1;
fio_fx86_m256i va = fio_fx86_256_loadu_si256(a);
fio_fx86_m256i vb = fio_fx86_256_loadu_si256(b);
fio_fx86_m256i eq = fio_fx86_256_cmpeq_epi8(va, vb);
int mask = fio_fx86_256_movemask_epi8(eq);
return ((mask & (1 << 7)) == 0) ? 0 : 1;
}
```
#### Shadowing x86 names
```c
#define FIO_FAKE_X86_SHADOW
#include "fio-stl.h"
#include
int main(void) {
uint32_t in[4] = {0x01020304, 0, 0, 0};
uint32_t out[4];
__m128i v = _mm_loadu_si128((const __m128i *)in);
v = _mm_slli_epi32(v, 8);
_mm_storeu_si128((__m128i *)out, v);
return out[0] == 0x02030400 ? 0 : 1;
}
```
### Safety Notes
#### Memory ownership
The fake x86 API does not allocate or free memory. Load and store functions copy fixed-size byte ranges from caller-owned buffers.
#### Thread safety
Functions are value-based and use only caller-provided memory plus read-only tables. They are thread-safe when callers synchronize shared buffers as usual.
#### Constant-time behavior
The portable AES fallback uses lookup tables and is not constant-time. SHA and PCLMUL fallbacks are portable C implementations intended for correctness and test coverage, not a promise of native-intrinsic timing.
#### Macro expansion
`FIO_FOR_UNROLL` declares loop variables inside the macro and expands `action` many times. Shadow-mode `_mm*` names are macros that call the `fio_fx86*` functions; avoid side-effect-heavy arguments even when they appear to be evaluated once today.
# Static Scratch Allocator
A tiny, statically-backed allocator for short-lived temporary buffers.
Use it when you need a quick buffer you can return to a caller that should not be freed by the caller, or as a safer alternative to `alloca` for data that only needs to stay valid until the next few function calls.
The implementation is in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
## `FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`
```c
#ifndef FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX
#define FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX 256
#endif
```
Default slot count for library allocators that explicitly pass it as the fourth argument to `FIO_STATIC_ALLOC_DEF`.
`FIO_STATIC_ALLOC_DEF` does not use this setting implicitly. For each allocator, choose its `max_concurrent_allocations` argument according to the number of short-lived slots that may be outstanding at once. Raise this setting only when an allocator uses it and its slots are being reused too early.
## `FIO_STATIC_ALLOC_DEF`
```c
#define FIO_STATIC_ALLOC_DEF(name, type_T, units_per_allocation, max_concurrent_allocations)
```
Defines a static allocator named `name`.
```c
static type_T *name(size_t count);
```
The generated function returns an aligned `type_T *` to a slot containing `units_per_allocation` elements. Its `count` argument advances the atomic round-robin position by that number of slots; it does not change the size of the returned slot. Passing `0` returns the first slot without advancing the position.
- `name` — the allocator function name.
- `type_T` — the element type of each slot.
- `units_per_allocation` — number of `type_T` elements in each slot.
- `max_concurrent_allocations` — number of round-robin slots available before reuse.
The logical arena capacity reported by `name##_size()` is:
```
max_concurrent_allocations * units_per_allocation
```
## Generated API
The macro produces two functions:
- **`type_T *name(size_t count)`** — returns a pointer to a slot selected from the static round-robin buffer. No matching free exists; the memory is static and reused automatically.
- **`size_t name##_size(void)`** — returns the logical arena capacity in `type_T` units.
There is no generated `name##_free`, `name##_reset`, or similar helper. The buffer is managed by an atomic position counter and wraps around after `max_concurrent_allocations` slots.
## Example
```c
FIO_STATIC_ALLOC_DEF(numer2hex_allocator,
char,
19,
FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX);
char *ntos16(uint16_t n) {
char *buf = numer2hex_allocator(1);
buf[0] = '0';
buf[1] = 'x';
fio_ltoa16u(buf + 2, n, 16);
buf[18] = 0;
return buf;
}
```
The returned string is valid only until the allocator wraps around. A similar pattern is used by `fiobj_num2cstr` for temporary conversions.
## Thread Safety
The allocator is only "good enough" thread-safe. The atomic position counter protects the round-robin index, but the safety window is bounded by the allocator's `max_concurrent_allocations` argument. Keep slots short-lived and do not hold a returned pointer across too many calls or threads.
## `FIO_STATIC_SAFE_ALLOC_DEF`
```c
#define FIO_STATIC_SAFE_ALLOC_DEF(name, type_T, units_per_allocation, max_concurrent_allocations)
```
A contention-safe variant of `FIO_STATIC_ALLOC_DEF` for short-lived scratch slots with an explicit checkout lifecycle. Where the round-robin variant silently reuses a slot once more than `max_concurrent_allocations` are outstanding (corrupting the in-flight user), this allocator returns `NULL` when every slot is busy, letting callers fall back gracefully.
Each slot carries a small metadata header holding its busy byte. The header is `min(sizeof(type_T), 16)` bytes: one element for small types (data naturally aligned for `type_T`), capped at 16 bytes for larger types (data 16-byte aligned). `type_T` alignment must be ≤ 16 (enforced at compile time).
### Generated API
- **`type_T *name##_try(void)`** — checks out a slot and returns a pointer to its data block (right after the metadata header), or `NULL` when all slots are busy.
- **`void name##_free(type_T *ptr)`** — releases a slot returned by `name##_try` (clears the busy byte).
- **`size_t name##_size(void)`** — logical arena capacity in `type_T` units.
### Example
```c
FIO_STATIC_SAFE_ALLOC_DEF(my_scratch, uint8_t, 4096, 16);
size_t work(const void *in, size_t len) {
uint8_t *slot = my_scratch_try();
if (!slot)
return 0; /* contention: caller falls back (e.g. uncompressed) */
size_t r = do_work(slot, in, len);
my_scratch_free(slot);
return r;
}
```
### Thread Safety
Slot checkout uses an atomic busy byte per slot with an atomic round-robin start hint; a checked-out slot is exclusively owned until `name##_free`. The busy-byte protocol is exact (no safety window) — the only failure mode is `NULL` under contention, which callers must handle.
# String and Buffer Information
Lightweight descriptors for byte ranges. They are defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) and are used throughout the library to pass around strings and buffers without taking ownership.
## Descriptor Types
### `fio_str_info_s`
A string descriptor that carries length, buffer pointer, and capacity.
```c
typedef struct fio_str_info_s {
size_t len; /* string length, in bytes */
char *buf; /* pointer to the first byte, or NULL on error */
size_t capa; /* buffer capacity; 0 means read-only */
} fio_str_info_s;
```
`capa` tells callers how much room is available in the underlying buffer. When `capa` is `0`, the buffer is considered read-only and must not be written to through the descriptor.
### `fio_buf_info_s`
A lighter buffer descriptor with no capacity field.
```c
typedef struct fio_buf_info_s {
size_t len; /* buffer length, in bytes */
char *buf; /* pointer to the first byte, may be NULL */
} fio_buf_info_s;
```
Use this when only a length/buffer pair is needed, such as for input data or temporary views.
## Construction Macros
### `FIO_STR_INFO0`
An all-zero `fio_str_info_s`.
```c
#define FIO_STR_INFO0 ((fio_str_info_s){0})
```
### `FIO_STR_INFO1(str)`
Build an `fio_str_info_s` from a NUL-terminated C string. Computes the length with `FIO_STRLEN`. If `str` is `NULL`, the length is `0`.
```c
#define FIO_STR_INFO1(str) \
((fio_str_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
### `FIO_STR_INFO2(str, length)`
Build an `fio_str_info_s` from a buffer and a known length.
```c
#define FIO_STR_INFO2(str, length) \
((fio_str_info_s){.len = (length), .buf = (str)})
```
### `FIO_STR_INFO3(str, length, capacity)`
Build a fully initialized `fio_str_info_s`.
```c
#define FIO_STR_INFO3(str, length, capacity) \
((fio_str_info_s){.len = (length), .buf = (str), .capa = (capacity)})
```
### `FIO_BUF_INFO0`
An all-zero `fio_buf_info_s`.
```c
#define FIO_BUF_INFO0 ((fio_buf_info_s){0})
```
### `FIO_BUF_INFO1(str)`
Build an `fio_buf_info_s` from a NUL-terminated C string. If `str` is `NULL`, the length is `0`.
```c
#define FIO_BUF_INFO1(str) \
((fio_buf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
### `FIO_BUF_INFO2(str, length)`
Build an `fio_buf_info_s` from a buffer and a known length.
```c
#define FIO_BUF_INFO2(str, length) \
((fio_buf_info_s){.len = (length), .buf = (str)})
```
## Equality
### `FIO_STR_INFO_IS_EQ(s1, s2)`
Returns a true value if two `fio_str_info_s` objects have the same content.
The comparison is a fast-path short-circuit: equal length and either both empty, the same pointer, or matching first byte and `FIO_MEMCMP` equal.
```c
#define FIO_STR_INFO_IS_EQ(s1, s2) \
((s1).len == (s2).len && \
(!(s1).len || (s1).buf == (s2).buf || \
((s1).buf && (s2).buf && (s1).buf[0] == (s2).buf[0] && \
!FIO_MEMCMP((s1).buf, (s2).buf, (s1).len))))
```
### `FIO_BUF_INFO_IS_EQ(s1, s2)`
Same comparison, routed to `FIO_STR_INFO_IS_EQ`.
```c
#define FIO_BUF_INFO_IS_EQ(s1, s2) FIO_STR_INFO_IS_EQ((s1), (s2))
```
## Conversion
### `FIO_BUF2STR_INFO(buf_info)`
Convert an `fio_buf_info_s` to an `fio_str_info_s`. The resulting descriptor has the same `len` and `buf`; `capa` is left as `0`.
```c
#define FIO_BUF2STR_INFO(buf_info) \
((fio_str_info_s){.len = (buf_info).len, .buf = (buf_info).buf})
```
### `FIO_STR2BUF_INFO(str_info)`
Convert an `fio_str_info_s` to an `fio_buf_info_s`, dropping the capacity.
```c
#define FIO_STR2BUF_INFO(str_info) \
((fio_buf_info_s){.len = (str_info).len, .buf = (str_info).buf})
```
## Temporary Stack Variables
### `FIO_STR_INFO_TMP_VAR(name, capacity)`
Creates a stack-allocated `fio_str_info_s` named `name` with `capacity` writable bytes. The macro allocates `capacity + 1` bytes so a trailing NUL guard is always present, and initializes `name.buf` and `name.capa`.
```c
#define FIO_STR_INFO_TMP_VAR(name, capacity) \
char fio___stack_mem___##name[(capacity) + 1]; \
fio___stack_mem___##name[0] = 0; /* guard */ \
fio_str_info_s name = (fio_str_info_s) { \
.buf = fio___stack_mem___##name, .capa = (capacity) \
}
```
Use this when a function needs a writable working buffer that normally fits on the stack but may be reallocated by downstream helpers.
### `FIO_STR_INFO_TMP_IS_REALLOCATED(name)`
Returns a true value if the temporary variable `name` no longer points to its original stack buffer, which indicates that a helper reallocated the memory onto the heap.
```c
#define FIO_STR_INFO_TMP_IS_REALLOCATED(name) \
(fio___stack_mem___##name != name.buf)
```
If this returns true, the buffer must eventually be freed by whatever allocator the helper used (usually the facil.io allocator or the override set via `FIO_MEM_REALLOC`).
# UTF-8 Helpers
Single-character encoding, decoding, and validation helpers for UTF-8 text. Defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
These functions work on one code point at a time and do not check memory bounds. Use them carefully — make sure the buffer has enough space for writes and is padded or NUL-terminated for reads.
## Compile-Time Flag
### `FIO_UTF8_ALLOW_IF`
Controls whether the UTF-8 helpers may use branchy (`if`) fast paths.
- **`1`** (default): ASCII-biased fast paths. Best when most input is ASCII.
- **`0`**: Branchless, constant-time-ish paths. Better when input distribution is unpredictable and branch mispredictions are a concern.
Defined before including [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) to override the default.
## Length Helpers
### `fio_utf8_code_len`
```c
unsigned fio_utf8_code_len(uint32_t u);
```
Returns the number of bytes required to encode code point `u` in UTF-8.
Returns `1`–`4` for code points up to `0x1FFFFF`, and returns `0` only above that bound.
This bound is above the valid Unicode range (`0x10FFFF`), so the function accepts code points in the range `0x110000`–`0x1FFFFF` as a consequence of its bitmask. `fio_utf8_write` will encode those code points as 4-byte sequences.
### `fio_utf8_char_len_unsafe`
```c
unsigned fio_utf8_char_len_unsafe(uint8_t c);
```
Classifies a single leading byte without validating continuation bytes.
Returns:
- `1`–`4`: the expected total byte length of the UTF-8 character.
- `8`: a continuation byte (middle of a multi-byte character).
- `0`: an invalid leading byte.
Use this only to recover length information after a successful `fio_utf8_char_len`, `fio_utf8_write`, or similar call where validity is already known.
### `fio_utf8_char_len`
```c
unsigned fio_utf8_char_len(const void *str);
```
Returns the number of valid UTF-8 bytes used by the first character at `str`, validating continuation bytes.
Returns `0` if `str` does not point to a valid UTF-8-encoded code point. Returns `1`–`4` for a valid character.
## Write Helper
### `fio_utf8_write`
```c
unsigned fio_utf8_write(void *dest, uint32_t u);
```
Writes the UTF-8 encoding of code point `u` to `dest`. Returns the number of bytes written (`0`–`4`).
`u` is treated as described for `fio_utf8_code_len`: code points up to `0x1FFFFF` are encoded (including the non-Unicode range `0x110000`–`0x1FFFFF`), and higher values result in `0` bytes written.
The caller must ensure `dest` has room for up to 4 bytes. Typical use advances the destination pointer:
```c
dest += fio_utf8_write(dest, u);
```
## Read Helper
### `fio_utf8_read`
```c
uint32_t fio_utf8_read(char **str);
```
Decodes the first UTF-8 character at `*str` and returns its code point. Advances `*str` by the number of bytes consumed.
Returns `0` if the byte at `*str` is not a valid UTF-8 start byte or continuation sequence.
## Peek Helper
### `fio_utf8_peek`
```c
uint32_t fio_utf8_peek(const char *str);
```
Decodes the first UTF-8 character at `str` and returns its code point. Does not modify `str`.
Returns `0` for invalid input, same as `fio_utf8_read`.
## See Also
- [`./000 core.md`](https://facil.io/0.8.x/facil-io-stl-core/) — overview of core helpers.
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth for the UTF-8 helpers.
- [`./001 string info.md`](https://facil.io/0.8.x/string-and-buffer-information/) — string and buffer descriptors used with text helpers.
# Vector Math
Wide integer unions and lane-wise arithmetic helpers defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/). These types are used for SIMD-style processing, big-integer arithmetic, hashing, and cryptography.
## Wide Union Types
These unions expose the same data through arrays of different widths, plus platform-specific vector views when SIMD is available. They are sized and aligned as follows:
| Type | Size | Alignment |
|------|------|-----------|
| `fio_u128` | 16 bytes | 16 |
| `fio_u256` | 32 bytes | 32 |
| `fio_u512` | 64 bytes | 64 |
| `fio_u1024` | 128 bytes | 64 |
| `fio_u2048` | 256 bytes | 64 |
| `fio_u4096` | 512 bytes | 64 |
All types include these overlapping members (counts vary by element width):
```c
size_t uz[N]; ssize_t iz[N];
uint64_t u64[N]; int64_t i64[N];
uint32_t u32[N]; int32_t i32[N];
uint16_t u16[N]; int16_t i16[N];
uint8_t u8[N]; int8_t i8[N];
float f[N];
double d[N];
long double ld[total_bytes / sizeof(long double)];
```
`total_bytes` is the type size from the table above, and each `N` is `total_bytes / sizeof(element)`. Larger types also contain smaller ones, for example `fio_u1024` provides `u128[8]`, `u256[4]`, and `u512[2]` in addition to the generic arrays.
When the compiler supports GCC/Clang `vector_size` attributes or ARM NEON intrinsics, the unions also expose vector views named `x64`, `x32`, `x16`, and `x8`. The internal layout adapts to the platform:
- On GCC/Clang, `x64[1]` is a single full-width vector.
- On ARM NEON, `x64[]` is an array of 128-bit `uint64x2_t` vectors.
- On platforms without vector support, `x64[]` falls back to a plain `uint64_t` array.
## Initialization Macros
Initialize a wide union from a list of lane values. Unused lanes are zeroed by the compiler.
```c
fio_u128_init8(0x01, 0x02, ...)
fio_u128_init16(...)
fio_u128_init32(...)
fio_u128_init64(...)
fio_u256_init8(...)
fio_u256_init16(...)
fio_u256_init32(...)
fio_u256_init64(...)
fio_u512_init8(...)
fio_u512_init16(...)
fio_u512_init32(...)
fio_u512_init64(...)
fio_u1024_init8(...)
fio_u1024_init16(...)
fio_u1024_init32(...)
fio_u1024_init64(...)
fio_u2048_init8(...)
fio_u2048_init16(...)
fio_u2048_init32(...)
fio_u2048_init64(...)
fio_u4096_init8(...)
fio_u4096_init16(...)
fio_u4096_init32(...)
fio_u4096_init64(...)
```
## Load, Store, and Byte Swap
All loads and stores copy whole vectors to and from memory using the fixed-size `fio_memcpyNN` helpers.
```c
fio_u128 fio_u128_load(const void *buf);
void fio_u128_store(void *buf, const fio_u128 a);
fio_u256 fio_u256_load(const void *buf);
void fio_u256_store(void *buf, const fio_u256 a);
fio_u512 fio_u512_load(const void *buf);
void fio_u512_store(void *buf, const fio_u512 a);
fio_u1024 fio_u1024_load(const void *buf);
void fio_u1024_store(void *buf, const fio_u1024 a);
fio_u2048 fio_u2048_load(const void *buf);
void fio_u2048_store(void *buf, const fio_u2048 a);
fio_u4096 fio_u4096_load(const void *buf);
void fio_u4096_store(void *buf, const fio_u4096 a);
```
Endian-aware load helpers read each lane from the given byte order and convert it to local order. Store variants are not provided here; use the byte-swap helpers before `store` if needed.
```c
fio_u128 fio_u128_load_le16(const void *buf);
fio_u128 fio_u128_load_be16(const void *buf);
fio_u128 fio_u128_load_le32(const void *buf);
fio_u128 fio_u128_load_be32(const void *buf);
fio_u128 fio_u128_load_le64(const void *buf);
fio_u128 fio_u128_load_be64(const void *buf);
fio_u256 fio_u256_load_le16(...); fio_u256 fio_u256_load_be16(...);
fio_u256 fio_u256_load_le32(...); fio_u256 fio_u256_load_be32(...);
fio_u256 fio_u256_load_le64(...); fio_u256 fio_u256_load_be64(...);
fio_u512 fio_u512_load_le16(...); fio_u512 fio_u512_load_be16(...);
fio_u512 fio_u512_load_le32(...); fio_u512 fio_u512_load_be32(...);
fio_u512 fio_u512_load_le64(...); fio_u512 fio_u512_load_be64(...);
fio_u1024 fio_u1024_load_le16(...); fio_u1024 fio_u1024_load_be16(...);
fio_u1024 fio_u1024_load_le32(...); fio_u1024 fio_u1024_load_be32(...);
fio_u1024 fio_u1024_load_le64(...); fio_u1024 fio_u1024_load_be64(...);
fio_u2048 fio_u2048_load_le16(...); fio_u2048 fio_u2048_load_be16(...);
fio_u2048 fio_u2048_load_le32(...); fio_u2048 fio_u2048_load_be32(...);
fio_u2048 fio_u2048_load_le64(...); fio_u2048 fio_u2048_load_be64(...);
fio_u4096 fio_u4096_load_le16(...); fio_u4096 fio_u4096_load_be16(...);
fio_u4096 fio_u4096_load_le32(...); fio_u4096 fio_u4096_load_be32(...);
fio_u4096 fio_u4096_load_le64(...); fio_u4096 fio_u4096_load_be64(...);
```
Byte-swap every lane of a vector, returning the result by value:
```c
fio_u128 fio_u128_bswap16(fio_u128 a);
fio_u128 fio_u128_bswap32(fio_u128 a);
fio_u128 fio_u128_bswap64(fio_u128 a);
fio_u256 fio_u256_bswap16(...); fio_u256 fio_u256_bswap32(...); fio_u256 fio_u256_bswap64(...);
fio_u512 fio_u512_bswap16(...); fio_u512 fio_u512_bswap32(...); fio_u512 fio_u512_bswap64(...);
fio_u1024 fio_u1024_bswap16(...); fio_u1024 fio_u1024_bswap32(...); fio_u1024 fio_u1024_bswap64(...);
fio_u2048 fio_u2048_bswap16(...); fio_u2048 fio_u2048_bswap32(...); fio_u2048 fio_u2048_bswap64(...);
fio_u4096 fio_u4096_bswap16(...); fio_u4096 fio_u4096_bswap32(...); fio_u4096 fio_u4096_bswap64(...);
```
## Lane-Wise Arithmetic
The arithmetic functions write lane-wise results into `*target`. They work on any `fio_uXXX` type at 8, 16, 32, and 64-bit lane widths. The compiler vectorizes the loops when SIMD is available.
```c
void fio_u128_add8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_sub8(...); void fio_u128_sub16(...); void fio_u128_sub32(...); void fio_u128_sub64(...);
void fio_u128_mul8(...); void fio_u128_mul16(...); void fio_u128_mul32(...); void fio_u128_mul64(...);
void fio_u128_and8(...); void fio_u128_and16(...); void fio_u128_and32(...); void fio_u128_and64(...);
void fio_u128_or8(...); void fio_u128_or16(...); void fio_u128_or32(...); void fio_u128_or64(...);
void fio_u128_xor8(...); void fio_u128_xor16(...); void fio_u128_xor32(...); void fio_u128_xor64(...);
```
These are generated for all wide types: `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
Constant-operand variants apply the same operation with a scalar in place of `*b`:
```c
void fio_u128_cadd8(fio_u128 *target, const fio_u128 *a, uint8_t b);
void fio_u128_cadd16(fio_u128 *target, const fio_u128 *a, uint16_t b);
void fio_u128_cadd32(fio_u128 *target, const fio_u128 *a, uint32_t b);
void fio_u128_cadd64(fio_u128 *target, const fio_u128 *a, uint64_t b);
void fio_u128_csub8(...); void fio_u128_csub16(...); void fio_u128_csub32(...); void fio_u128_csub64(...);
void fio_u128_cmul8(...); void fio_u128_cmul16(...); void fio_u128_cmul32(...); void fio_u128_cmul64(...);
void fio_u128_cand8(...); void fio_u128_cand16(...); void fio_u128_cand32(...); void fio_u128_cand64(...);
void fio_u128_cor8(...); void fio_u128_cor16(...); void fio_u128_cor32(...); void fio_u128_cor64(...);
void fio_u128_cxor8(...); void fio_u128_cxor16(...); void fio_u128_cxor32(...); void fio_u128_cxor64(...);
```
Reduction functions fold all lanes of the given width into a single scalar:
```c
uint8_t fio_u128_reduce_add8(const fio_u128 *a);
uint16_t fio_u128_reduce_add16(const fio_u128 *a);
uint32_t fio_u128_reduce_add32(const fio_u128 *a);
uint64_t fio_u128_reduce_add64(const fio_u128 *a);
uint8_t fio_u128_reduce_sub8(...); uint16_t fio_u128_reduce_sub16(...);
uint32_t fio_u128_reduce_sub32(...); uint64_t fio_u128_reduce_sub64(...);
uint8_t fio_u128_reduce_mul8(...); uint16_t fio_u128_reduce_mul16(...);
uint32_t fio_u128_reduce_mul32(...); uint64_t fio_u128_reduce_mul64(...);
uint8_t fio_u128_reduce_and8(...); uint16_t fio_u128_reduce_and16(...);
uint32_t fio_u128_reduce_and32(...); uint64_t fio_u128_reduce_and64(...);
uint8_t fio_u128_reduce_or8(...); uint16_t fio_u128_reduce_or16(...);
uint32_t fio_u128_reduce_or32(...); uint64_t fio_u128_reduce_or64(...);
uint8_t fio_u128_reduce_xor8(...); uint16_t fio_u128_reduce_xor16(...);
uint32_t fio_u128_reduce_xor32(...); uint64_t fio_u128_reduce_xor64(...);
```
Whole-vector variants that operate on 64-bit lanes without a width suffix are also provided for bitwise operations:
```c
void fio_u128_and(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_or (fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
```
## Rotations
Lane-wise right and left rotations. Variable rotations read the per-lane shift from an array; constant rotations use a single `uint8_t` for all lanes.
```c
void fio_u128_rrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t rotations[16]);
void fio_u128_rrot16(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[8]);
void fio_u128_rrot32(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[4]);
void fio_u128_rrot64(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[2]);
void fio_u128_lrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t rotations[16]);
void fio_u128_lrot16(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[8]);
void fio_u128_lrot32(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[4]);
void fio_u128_lrot64(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[2]);
void fio_u128_crrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
```
All rotation families exist for `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096` with appropriate array sizes.
## Ternary Lane-Wise Operations
These compute common three-input bit functions across each lane.
```c
// target[i] = z[i] ^ (x[i] & (y[i] ^ z[i]))
void fio_u128_mux32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_mux64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
// target[i] = (x[i] & y[i]) | (z[i] & (x[i] | y[i]))
void fio_u128_maj32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_maj64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
// target[i] = x[i] ^ y[i] ^ z[i]
void fio_u128_3xor32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_3xor64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
```
Whole-vector 64-bit variants without a width suffix are also provided:
```c
void fio_u128_mux(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_maj(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_3xor(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
```
All ternary functions exist for the other wide types: `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
## Whole-Vector Value Operations
A small set of convenience functions return results by value instead of writing through a pointer. They operate on 64-bit lanes and are designed to let the compiler use SIMD directly.
```c
fio_u128 fio_u128_xorv(fio_u128 a, fio_u128 b);
fio_u128 fio_u128_andv(fio_u128 a, fio_u128 b);
fio_u128 fio_u128_orv (fio_u128 a, fio_u128 b);
fio_u128 fio_u128_addv64(fio_u128 a, fio_u128 b);
fio_u256 fio_u256_xorv(...); fio_u256 fio_u256_andv(...); fio_u256 fio_u256_orv(...); fio_u256 fio_u256_addv64(...);
fio_u512 fio_u512_xorv(...); fio_u512 fio_u512_andv(...); fio_u512 fio_u512_orv(...); fio_u512 fio_u512_addv64(...);
fio_u1024 fio_u1024_xorv(...); fio_u1024 fio_u1024_andv(...); fio_u1024 fio_u1024_orv(...); fio_u1024 fio_u1024_addv64(...);
fio_u2048 fio_u2048_xorv(...); fio_u2048 fio_u2048_andv(...); fio_u2048 fio_u2048_orv(...); fio_u2048 fio_u2048_addv64(...);
fio_u4096 fio_u4096_xorv(...); fio_u4096 fio_u4096_andv(...); fio_u4096 fio_u4096_orv(...); fio_u4096 fio_u4096_addv64(...);
```
## Constant-Time Helpers
```c
bool fio_u128_is_eq(const fio_u128 *a, const fio_u128 *b);
```
Returns `true` if every byte of `*a` equals the corresponding byte of `*b`, otherwise `false`. The comparison is branchless over the vector body.
```c
void fio_u128_inv(fio_u128 *target, const fio_u128 *a);
```
Bitwise NOT: `target[i] = ~a[i]` on 64-bit lanes.
```c
void fio_u128_ct_swap_if(bool cond, fio_u128 *a, fio_u128 *b);
```
Swaps `*a` and `*b` when `cond` is true, without branching on `cond`. Uses a mask derived from `(uint64_t)0 - cond`.
All three helpers exist for `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
## Multi-Precision Math
These functions treat the wide unions as unsigned big integers made of little-endian `u64` words.
```c
uint64_t fio_u128_add(fio_u128 *result, const fio_u128 *a, const fio_u128 *b);
uint64_t fio_u128_sub(fio_u128 *result, const fio_u128 *a, const fio_u128 *b);
int fio_u128_cmp(const fio_u128 *a, const fio_u128 *b);
uint64_t fio_u256_add(...); uint64_t fio_u256_sub(...); int fio_u256_cmp(...);
uint64_t fio_u512_add(...); uint64_t fio_u512_sub(...); int fio_u512_cmp(...);
uint64_t fio_u1024_add(...); uint64_t fio_u1024_sub(...); int fio_u1024_cmp(...);
uint64_t fio_u2048_add(...); uint64_t fio_u2048_sub(...); int fio_u2048_cmp(...);
uint64_t fio_u4096_add(...); uint64_t fio_u4096_sub(...); int fio_u4096_cmp(...);
```
- `fio_uXXX_add` returns the carry bit (`1` or `0`).
- `fio_uXXX_sub` returns the borrow bit (`1` or `0`).
- `fio_uXXX_cmp` returns `-1` if `a < b`, `0` if equal, and `1` if `a > b`.
Multiplication doubles the width of the result:
```c
void fio_u128_mul(fio_u256 *result, const fio_u128 *a, const fio_u128 *b);
void fio_u256_mul(fio_u512 *result, const fio_u256 *a, const fio_u256 *b);
void fio_u512_mul(fio_u1024 *result, const fio_u512 *a, const fio_u512 *b);
void fio_u1024_mul(fio_u2048 *result, const fio_u1024 *a, const fio_u1024 *b);
void fio_u2048_mul(fio_u4096 *result, const fio_u2048 *a, const fio_u2048 *b);
```
Montgomery multiplication is provided for RSA-like modular arithmetic:
```c
void fio_u128_montgomery_mul(fio_u128 *result, const fio_u128 *a, const fio_u128 *b,
const fio_u128 *N, const fio_u128 *N_dash);
void fio_u256_montgomery_mul(fio_u256 *result, const fio_u256 *a, const fio_u256 *b,
const fio_u256 *N, const fio_u256 *N_dash);
void fio_u512_montgomery_mul(fio_u512 *result, const fio_u512 *a, const fio_u512 *b,
const fio_u512 *N, const fio_u512 *N_dash);
void fio_u1024_montgomery_mul(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b,
const fio_u1024 *N, const fio_u1024 *N_dash);
void fio_u2048_montgomery_mul(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b,
const fio_u2048 *N, const fio_u2048 *N_dash);
```
The multiplication uses `fio_math_mul` from [`./002 math.h`](https://facil.io/0.8.x/api/002-math/).
## Shuffle and Reduce on Native Arrays
A separate family of helpers works on plain C arrays of fixed size. They are useful when the data is not wrapped in a `fio_uXXX` union.
### Shuffle
```c
void fio_u8x4_reshuffle (uint8_t *v, uint8_t indx[4]);
void fio_u8x8_reshuffle (uint8_t *v, uint8_t indx[8]);
void fio_u8x16_reshuffle (uint8_t *v, uint8_t indx[16]);
void fio_u8x32_reshuffle (uint8_t *v, uint8_t indx[32]);
void fio_u8x64_reshuffle (uint8_t *v, uint8_t indx[64]);
void fio_u8x128_reshuffle(uint8_t *v, uint8_t indx[128]);
void fio_u8x256_reshuffle(uint8_t *v, uint8_t indx[256]);
void fio_u16x2_reshuffle (uint16_t *v, uint8_t indx[2]);
void fio_u16x4_reshuffle (uint16_t *v, uint8_t indx[4]);
void fio_u16x8_reshuffle (uint16_t *v, uint8_t indx[8]);
void fio_u16x16_reshuffle(uint16_t *v, uint8_t indx[16]);
void fio_u16x32_reshuffle(uint16_t *v, uint8_t indx[32]);
void fio_u16x64_reshuffle(uint16_t *v, uint8_t indx[64]);
void fio_u16x128_reshuffle(uint16_t *v, uint8_t indx[128]);
void fio_u32x2_reshuffle (uint32_t *v, uint8_t indx[2]);
void fio_u32x4_reshuffle (uint32_t *v, uint8_t indx[4]);
void fio_u32x8_reshuffle (uint32_t *v, uint8_t indx[8]);
void fio_u32x16_reshuffle(uint32_t *v, uint8_t indx[16]);
void fio_u32x32_reshuffle(uint32_t *v, uint8_t indx[32]);
void fio_u32x64_reshuffle(uint32_t *v, uint8_t indx[64]);
void fio_u64x2_reshuffle (uint64_t *v, uint8_t indx[2]);
void fio_u64x4_reshuffle (uint64_t *v, uint8_t indx[4]);
void fio_u64x8_reshuffle (uint64_t *v, uint8_t indx[8]);
void fio_u64x16_reshuffle(uint64_t *v, uint8_t indx[16]);
void fio_u64x32_reshuffle(uint64_t *v, uint8_t indx[32]);
void fio_floatx2_reshuffle (float *v, uint8_t indx[2]);
void fio_floatx4_reshuffle (float *v, uint8_t indx[4]);
void fio_floatx8_reshuffle (float *v, uint8_t indx[8]);
void fio_floatx16_reshuffle(float *v, uint8_t indx[16]);
void fio_floatx32_reshuffle(float *v, uint8_t indx[32]);
void fio_floatx64_reshuffle(float *v, uint8_t indx[64]);
void fio_dblx2_reshuffle (double *v, uint8_t indx[2]);
void fio_dblx4_reshuffle (double *v, uint8_t indx[4]);
void fio_dblx8_reshuffle (double *v, uint8_t indx[8]);
void fio_dblx16_reshuffle (double *v, uint8_t indx[16]);
void fio_dblx32_reshuffle (double *v, uint8_t indx[32]);
```
Indices are masked with `len - 1`, so out-of-range indices wrap safely.
Variadic macro wrappers let you pass the index list inline:
```c
fio_u8x4_reshuffle(v, 3, 2, 1, 0);
fio_u32x4_reshuffle(v, 0, 2, 1, 3);
```
### Reduce
Integer reductions provide add, subtract, multiply, bitwise and/or/xor, and min/max:
```c
uint8_t fio_u8x4_reduce_add (uint8_t *v); uint8_t fio_u8x4_reduce_sub (uint8_t *v);
uint8_t fio_u8x4_reduce_mul (uint8_t *v); uint8_t fio_u8x4_reduce_and (uint8_t *v);
uint8_t fio_u8x4_reduce_or (uint8_t *v); uint8_t fio_u8x4_reduce_xor (uint8_t *v);
uint8_t fio_u8x4_reduce_max (uint8_t *v); uint8_t fio_u8x4_reduce_min (uint8_t *v);
// Same pattern for u8 x 8, 16, 32, 64, 128, 256
// Same pattern for u16 x 2, 4, 8, 16, 32, 64, 128
// Same pattern for u32 x 2, 4, 8, 16, 32, 64
// Same pattern for u64 x 2, 4, 8, 16, 32
```
Floating-point reductions provide add, multiply, and min/max (no bitwise operations):
```c
float fio_floatx2_reduce_add(float *v); float fio_floatx2_reduce_mul(float *v);
float fio_floatx2_reduce_max(float *v); float fio_floatx2_reduce_min(float *v);
// Same pattern for float x 4, 8, 16, 32, 64
// Same pattern for dbl x 2, 4, 8, 16, 32
```
### Lane-wise operations on native arrays
Each integer reduction family also defines matching in-place binary operators on the arrays themselves:
```c
void fio_u8x4_add (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_sub (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_mul (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_and (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_or (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_xor (uint8_t *dest, uint8_t *a, uint8_t *b);
// Same pattern for all u8/u16/u32/u64 widths and lengths.
```
Floating-point array families define add and multiply only:
```c
void fio_floatx2_add(float *dest, float *a, float *b);
void fio_floatx2_mul(float *dest, float *a, float *b);
// Same pattern for float x 4, 8, 16, 32, 64 and dbl x 2, 4, 8, 16, 32.
```
## See Also
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — source of truth for all vector definitions.
- [`./000 core.md`](https://facil.io/0.8.x/facil-io-stl-core/) — core documentation index.
- `001 fx86.h` — fake/portable x86 intrinsics built on these types.
- [`./002 math.h`](https://facil.io/0.8.x/api/002-math/) — multi-precision `fio_math_mul` and related helpers.
# Version and Settings
The knobs that control how facil.io identifies itself and behaves at compile time.
For core types and everyday helpers, see [`./000 core.md`](https://facil.io/0.8.x/facil-io-stl-core/). This page covers version macros and core behavioral defaults; for per-inclusion hooks such as allocator routing and pointer tagging, see [`./001 header.h`](https://facil.io/0.8.x/api/001-header/).
## Version Macros
facil.io follows [semantic versioning](https://semver.org). These macros are defined in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) and can be used to detect the library version at compile time.
- **`FIO_VERSION_MAJOR`** — Major version. API/ABI breaking changes bump this.
Default: `0`
- **`FIO_VERSION_MINOR`** — Minor version. Significant features or deprecations. May break ABI.
Default: `8`
- **`FIO_VERSION_PATCH`** — Patch version. Bug fixes and small additions.
Default: `0`
- **`FIO_VERSION_BUILD`** — Optional build metadata, such as `"rc.02"`.
Default: `"rc.02"`
- **`FIO_VERSION_STRING`** — Full version as a string literal, built from the macros above. The guard is `#ifdef FIO_VERSION_BUILD`, so the build metadata is included whenever `FIO_VERSION_BUILD` is defined, including when defined as an empty string: `"0.8.0-rc.02"`. If `FIO_VERSION_BUILD` is undefined, the string reads `"0.8.0"`.
Because [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) unconditionally `#define`s `FIO_VERSION_BUILD "rc.02"` before the `#ifdef` guard, omitting the suffix requires `#undef FIO_VERSION_BUILD` before inclusion, not merely leaving it undefined.
Override these only when you are vendoring a fork or need to report a custom build string.
## Compile-Time Behavioral Defaults
Most defaults are guarded with `#ifndef`; define them before including the header to override the default. `FIO_NO_LOG` and `DEBUG` are presence flags rather than `#ifndef` defaults.
### Memory and Safety
- **`FIO_LEAK_COUNTER`** — Enables memory-leak counting. When enabled, the leak counter tracks allocations made through facil.io allocators and prints a summary at exit.
Default: `1` (enabled).
Set to `0` to disable counting and the exit report.
Note: this conflicts with `FIO_NO_LOG`, because the leak report uses the logging layer.
- **`FIO_LEAK_COUNTER_SKIP_EXIT`** — Skip the automatic leak report at exit when leak counting is enabled.
Default: `0` (report at exit).
- **`FIO_LEAK_COUNTER_DEF(name)`** — Declares a named leak counter.
- **`FIO_LEAK_COUNTER_ON_ALLOC(name)`** — Records an allocation against the named counter.
- **`FIO_LEAK_COUNTER_ON_FREE(name)`** — Records a free against the named counter.
- **`FIO_LEAK_COUNTER_COUNT(name)`** — Returns the current value of the named counter.
- **`FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`** — When non-zero, facil.io allocators zero-initialize returned memory by default.
Default: `1` (secure by default).
Set to `0` if you want raw allocations and prefer to do your own initialization.
- **`FIO_MEM_PAGE_SIZE_LOG`** — Log₂ of the OS memory page size. The memory allocator uses this to round allocations and map memory efficiently.
Default: `12` (4096-byte pages).
Change it if you are targeting a system with a different page size.
- **`FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`** — Default slot count for selected internal `FIO_STATIC_ALLOC_DEF` invocations (default: `256`). The macro does not use this setting implicitly; pass it explicitly as the fourth argument where appropriate.
### Concurrency
- **`FIO_USE_THREAD_MUTEX`** — Choose the locking primitive used by thread-safe modules.
Default: `0` (use facil.io spinlocks).
Set to `1` to use OS native mutexes (`pthread_mutex_t` on POSIX) instead.
If you need this decision to apply to only a single `#include`, use `FIO_USE_THREAD_MUTEX_TMP` in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) instead.
### Performance and Alignment
- **`FIO_UNALIGNED_ACCESS`** — Allows facil.io to attempt unaligned memory access on CPUs that support it. When enabled, the library may use faster paths in helpers such as `fio_buf2uXX`.
Default: `1` (attempt detection).
Set to `0` on strict-alignment architectures or if you want to forbid unaligned access entirely. The actual capability is reflected by `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED`, which is derived at compile time.
- **`FIO_LIMIT_INTRINSIC_BUFFER`** — Limits register pressure in some pseudo-intrinsic loops by processing smaller batches and looping more.
Default: `1`.
Set to `0` if you prefer larger unrolled buffers and do not mind the extra register use.
### Maps
- **`FIO_MAP_WARNING_BITSIZE`** — Bit size at which hash-map allocations trigger an internal warning threshold in the `imap`/`map` templates.
Default: `24`.
Raise or lower it to tune when the library starts worrying about map size during growth.
### Logging
- **`FIO_NO_LOG`** — Define this to remove all logging output. Cannot be combined with `FIO_LEAK_COUNTER`; the two are mutually exclusive.
- **`FIO_LOG2STDERR`** — Low-level sink used by the logging macros.
Default: no-op.
Override to print to `stderr` or route messages to a custom destination.
- **`FIO_LOG_LENGTH_LIMIT`** — Approximate cap on a single formatted log message, limiting stack memory use.
Default: `1024`.
- **`DEBUG`** — Define this to enable debug-only log macros (`FIO_LOG_D*`) and active `FIO_ASSERT_DEBUG` checks. Also disables SIMD/Neon intrinsics in [`./000 core.h`](https://facil.io/0.8.x/api/000-core/).
### Memory allocator routing
These macros are defined in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) and are re-evaluated on every inclusion cycle. Define them before the first fio-stl include to replace memory routing globally.
- **`FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len)`** — Reallocate a block, copying at least `copy_len` bytes if a move is required. Defaults to the system `realloc` unless the facil.io allocator is available, in which case it routes to `fio_realloc2`. Override to plug in a custom allocator.
- **`FIO_MEM_FREE(ptr, size)`** — Free a block. Defaults to the system `free` or `fio_free`. Override alongside `FIO_MEM_REALLOC`.
- **`FIO_MEMORY_DISABLE`** — Define to disable all custom facil.io allocators and force system `malloc`/`free` for temporary allocations.
- **`FIO_MALLOC_TMP_USE_SYSTEM`** — Force the recursive allocator variant to use the system `realloc`/`free`. Useful when a custom `FIO_MEM_REALLOC` needs an allocation path that will not recurse back into facil.io.
- **`FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`** — Recursive-safe copies of `FIO_MEM_REALLOC`/`FIO_MEM_FREE`. Override these when the custom allocator itself needs to allocate or free memory without re-entering the override.
- **`FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment)`** — Reallocate with a minimum pointer alignment, **assigning** the result to `ptr` (save a copy first if failure must be recoverable). `alignment` of `0` selects the allocator default; non power-of-2 values are rounded down to a power of 2; effective alignment never drops below the current alignment of `ptr` nor below the allocator default. Routes to `fio_realloc_aligned` when the facil.io allocator is available, or to the system backends (`_aligned_realloc` on Windows, `posix_memalign`-based emulation on POSIX).
- **`FIO_MEM_FREE_ALIGNED(ptr, size)`** — Free a block obtained through `FIO_MEM_REALLOC_ALIGNED`. With the facil.io allocator this is simply `fio_free` (which accepts interior pointers); with the system backends it routes to `_aligned_free` on Windows or `free` on POSIX. Custom allocators that stash alignment bookkeeping near the returned pointer should override this pair together.
- **`FIO_MEM_ALLOC_SIZE(size)`** — The usable bytes the allocator reserves for a `size` request (`fio_alloc_size`, or the identity function with the system allocator).
- **`FIO_MEM_REALLOC_ALIGNED_` / `FIO_MEM_FREE_ALIGNED_`** — Recursive-safe copies of the aligned pair, mirroring `FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`.
### Pointer tagging
These macros are defined in [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) and let included types store a small marker in the unused low bits of aligned pointers.
- **`FIO_PTR_TAG(p)`** — Apply the tag to a pointer. Default is the identity function.
- **`FIO_PTR_UNTAG(p)`** — Remove the tag from a pointer. Default is the identity function.
- **`FIO_PTR_TAG_TYPE`** — If defined, functions returning a type's pointer return this type instead.
- **`FIO_PTR_TAG_VALIDATE(ptr)`** — Returns a true value if the tagged pointer is valid. Default checks for non-`NULL`.
- **`FIO_PTR_TAG_VALID_OR_RETURN(tagged_ptr, value)`** — Validates the tagged pointer and returns `value` if it fails validation.
- **`FIO_PTR_TAG_VALID_OR_RETURN_VOID(tagged_ptr)`** — Validates the tagged pointer and returns `void` if it fails validation.
- **`FIO_PTR_TAG_VALID_OR_GOTO(tagged_ptr, label)`** — Validates the tagged pointer and jumps to `label` if it fails validation.
- **`FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr)`** — Cast the untagged pointer to `untagged_type *`.
Pointer tagging is used by dynamic-type wrappers (for example, FIOBJ) to distinguish object kinds without a separate type field.
## See Also
- [`./000 core.md`](https://facil.io/0.8.x/facil-io-stl-core/) — documentation for core types and helpers.
- [`./000 core.h`](https://facil.io/0.8.x/api/000-core/) — the source of truth for version macros and the defaults above.
- [`./001 header.h`](https://facil.io/0.8.x/api/001-header/) — per-inclusion overrides such as `FIO_USE_THREAD_MUTEX_TMP` and allocator routing macros.
# String / Number Conversion
```c
#define FIO_ATOL
#include "fio-stl.h"
```
String-to-number and number-to-string helpers. These are the grunts that parse integers, floats, hex, binary, and arbitrary bases, then turn them back into text. Fast, greedy, and mostly guard-less — give them a valid buffer and a terminating character.
**Note:** functions that write to a buffer also write a NUL terminator. `fio_atol*` functions assume the buffer ends with an invalid character (such as NUL) and that allocations are aligned enough for multi-byte reads.
**Guard-byte contract:** parsing may read up to - and including - the first non-numeric byte following a number. Callers MUST keep the buffer readable through that byte: pass a NUL-terminated string or append a non-numeric guard byte. `fio_bstr` strings always satisfy this contract. Exact-size buffers with no readable byte past the number (e.g., a 1-byte allocation holding `"1"`) violate it and will trigger AddressSanitizer (sanitize with `FIO_MEMORY_DISABLE` for visibility).
### Configuration Macros
#### `FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER`
```c
#define FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER 1
```
When `1` (default), underscores act as digit separators: `1_000_000` parses as `1000000`. Set to `0` to disable.
### Types
#### `fio_aton_s`
```c
typedef struct {
union {
int64_t i;
double f;
uint64_t u;
};
int is_float;
int err;
} fio_aton_s;
```
Result container for `fio_aton`. Read the union member that matches `is_float`, and check `err` for overflow or parse failures.
### Universal Parsing
#### `fio_aton`
```c
FIO_SFUNC fio_aton_s fio_aton(char **pstr);
```
Auto-detects integers and floats. Skips leading whitespace, recognizes `0x` / `0b` / octal prefixes, and accepts `inf`, `infinity`, and `nan`. Updates `*pstr` to the first unconverted character. Sets `.err` on overflow or bad format.
**Note:** not an exact `strtod` replacement; rounding differences are possible.
### Strict, Bounded Parsing (wire-safe)
The `fio_stol*` family parses within an explicit `[pos, end)` window and accepts **only** the wire format: no whitespace, no underscores, no `0x`/`0b` prefixes, no `inf`/`nan`. Use these for network protocols and any length/size field - the lenient `fio_atol*` semantics (e.g., `1_0` parsing as `10`) are a differential-parsing hazard when a peer or intermediary parses the same field strictly.
All three advance `*pos` past the digits consumed (detect trailing junk or empty input by comparing `*pos` against expectations) and set `errno == E2BIG` on overflow, stopping at the last valid digit. Since the window is explicit, the guard-byte contract of the lenient family does not apply.
#### `fio_stol10u`
```c
SFUNC uint64_t fio_stol10u(char **pos, const char *end);
```
Parses an unsigned `uint64_t`, decimal digits only. No sign, no whitespace skip.
#### `fio_stol10`
```c
SFUNC int64_t fio_stol10(char **pos, const char *end);
```
Parses a signed `int64_t`: one optional leading `-` / `+`, then decimal digits only. `E2BIG` is set when the magnitude overflows `uint64_t` or exceeds the `int64_t` range (note: `-9223372036854775808` is exactly `INT64_MIN` and is **not** an overflow).
#### `fio_stol16u`
```c
SFUNC uint64_t fio_stol16u(char **pos, const char *end);
```
Parses an unsigned `uint64_t`, hex digits only - unlike `fio_atol16u`, a `0x` prefix is **not** consumed.
### Signed Conversion
#### `fio_atol`
```c
SFUNC int64_t fio_atol(char **pstr);
```
Parses a signed `int64_t`. Accepts base 10, octal (`0...`), hex (`0x...` or `x...`), and binary (`0b...` or `b...`). Updates `*pstr` past the number.
#### `fio_atof`
```c
SFUNC double fio_atof(char **pstr);
```
Parses a double. Wraps `strtod` for most inputs. The source also attempts to accept a raw `0b...` binary bit-pattern, but the detection condition looks fragile.
#### `fio_ftoa`
```c
SFUNC size_t fio_ftoa(char *dest, double num, uint8_t base);
```
Writes `num` to `dest` in `base` (2, 10, or 16; unsupported bases silently fall back to 10). No prefixes are added. Returns bytes written excluding NUL.
**Note:** provide at least 130 bytes for base 2. Special values `inf` and `nan` produce `"Infinity"` / `"NaN"`.
#### `fio_ltoa`
```c
SFUNC size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
```
Writes `num` to `dest` in `base` (2, 8, 10, 16, or any base up to 36). Adds `0x`, `0b`, or `0` prefixes for the built-in bases. Returns bytes written excluding NUL. If `dest` is `NULL`, writes to an internal scratch buffer and still returns the length. Logs an error and returns `0` for unsupported bases.
**Note:** provide at least 68 bytes for base 2.
#### `fio_ltoa10`
```c
FIO_IFUNC void fio_ltoa10(char *dest, int64_t i, size_t digits);
```
Writes a signed base-10 number using exactly `digits` bytes plus NUL. Use `fio_digits10()` to compute `digits`.
#### `fio_atol10`
```c
SFUNC int64_t fio_atol10(char **pstr);
```
Reads a signed base-10 number.
### Unsigned Conversion
#### `fio_atol8u`
```c
SFUNC uint64_t fio_atol8u(char **pstr);
```
Reads an unsigned octal number. May overflow the buffer if no terminator is present.
#### `fio_atol10u`
```c
SFUNC uint64_t fio_atol10u(char **pstr);
```
Reads an unsigned base-10 number.
#### `fio_atol16u`
```c
SFUNC uint64_t fio_atol16u(char **pstr);
```
Reads an unsigned hex number, with optional `0x` prefix.
#### `fio_atol_bin`
```c
SFUNC uint64_t fio_atol_bin(char **pstr);
```
Reads an unsigned binary number, with optional `0b` prefix.
#### `fio_atol_xbase`
```c
SFUNC uint64_t fio_atol_xbase(char **pstr, size_t base);
```
Reads an unsigned number in any base up to 36.
#### `fio_ltoa8u`
```c
FIO_IFUNC void fio_ltoa8u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned octal number using `digits` bytes plus NUL.
#### `fio_ltoa10u`
```c
FIO_IFUNC void fio_ltoa10u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned base-10 number using `digits` bytes plus NUL.
#### `fio_ltoa16u`
```c
FIO_IFUNC void fio_ltoa16u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned hex number using `digits` bytes plus NUL. `digits` is rounded up to an even number.
#### `fio_ltoa_bin`
```c
FIO_IFUNC void fio_ltoa_bin(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned binary number using `digits` bytes plus NUL.
#### `fio_ltoa_xbase`
```c
FIO_IFUNC void fio_ltoa_xbase(char *dest,
uint64_t i,
size_t digits,
size_t base);
```
Writes an unsigned number in `base` (up to 36) using `digits` bytes plus NUL.
### Helpers
#### `fio_c2i`
```c
IFUNC uint8_t fio_c2i(unsigned char c);
```
Maps a character to its numeric value (`0-9` → 0-9, `A-Z`/`a-z` → 10-35). Returns `255` for out-of-range characters.
#### `fio_i2c`
```c
IFUNC uint8_t fio_i2c(unsigned char i);
```
Maps a numeric value `0-35` to a character (`0-9`, `A-Z`). Out-of-range values above 35 produce undefined behavior; accepts values up to 63 by masking.
#### `fio_digits10`
```c
FIO_IFUNC size_t fio_digits10(int64_t i);
```
Returns the number of base-10 digits needed for `i`, including the sign.
#### `fio_digits10u`
```c
FIO_SFUNC size_t fio_digits10u(uint64_t i);
```
Returns the number of base-10 digits needed for an unsigned number.
#### `fio_digits8u`
```c
FIO_SFUNC size_t fio_digits8u(uint64_t i);
```
Returns the number of base-8 digits needed for an unsigned number.
#### `fio_digits16u`
```c
FIO_SFUNC size_t fio_digits16u(uint64_t i);
```
Returns the number of base-16 digits needed for an unsigned number, always an even count (2, 4, 6, ... 16).
#### `fio_digits_bin`
```c
FIO_SFUNC size_t fio_digits_bin(uint64_t i);
```
Returns the number of base-2 digits needed for an unsigned number, rounded up to an even count.
#### `fio_digits_xbase`
```c
FIO_SFUNC size_t fio_digits_xbase(uint64_t i, size_t base);
```
Returns the number of digits needed for an unsigned number in `base` (must be < 65).
#### `fio_u2i_limit`
```c
FIO_IFUNC int64_t fio_u2i_limit(uint64_t val, size_t invert);
```
Converts unsigned `val` to signed with overflow protection. If `invert` is zero, clamps to `INT64_MAX` and sets `errno = E2BIG` on overflow. If `invert` is non-zero, produces the negative value and clamps to `INT64_MIN`.
### IEEE 754 Helpers
#### `fio_i2d`
```c
FIO_IFUNC double fio_i2d(int64_t mant, int64_t exponent_in_base_2);
```
Converts a signed 64-bit mantissa and base-2 exponent to a `double`.
#### `fio_u2d`
```c
FIO_IFUNC double fio_u2d(uint64_t mant, int64_t exponent_in_base_2);
```
Converts an unsigned 64-bit mantissa and base-2 exponent to a `double`.
### Big Number Hex Conversion
#### `fio_u128_hex_read` / `fio_u256_hex_read` / `fio_u512_hex_read`
```c
SFUNC fio_u128 fio_u128_hex_read(char **pstr);
SFUNC fio_u256 fio_u256_hex_read(char **pstr);
SFUNC fio_u512 fio_u512_hex_read(char **pstr);
```
Reads a hex string and initializes the corresponding wide integer. Updates `*pstr` past the consumed input.
#### `fio_u1024_hex_read` / `fio_u2048_hex_read` / `fio_u4096_hex_read`
```c
SFUNC fio_u1024 fio_u1024_hex_read(char **pstr);
SFUNC fio_u2048 fio_u2048_hex_read(char **pstr);
SFUNC fio_u4096 fio_u4096_hex_read(char **pstr);
```
Same as above for 1024-, 2048-, and 4096-bit integers.
#### `fio_u128_hex_write` / `fio_u256_hex_write` / `fio_u512_hex_write`
```c
SFUNC size_t fio_u128_hex_write(char *dest, const fio_u128 *u);
SFUNC size_t fio_u256_hex_write(char *dest, const fio_u256 *u);
SFUNC size_t fio_u512_hex_write(char *dest, const fio_u512 *u);
```
Writes a wide integer as a hex string to `dest`. Returns bytes written excluding NUL.
#### `fio_u1024_hex_write` / `fio_u2048_hex_write` / `fio_u4096_hex_write`
```c
SFUNC size_t fio_u1024_hex_write(char *dest, const fio_u1024 *u);
SFUNC size_t fio_u2048_hex_write(char *dest, const fio_u2048 *u);
SFUNC size_t fio_u4096_hex_write(char *dest, const fio_u4096 *u);
```
Same as above for 1024-, 2048-, and 4096-bit integers.
### Example
```c
#define FIO_ATOL
#include "fio-stl.h"
int main(void) {
char *p = "0x1F 0b1010 42";
char buf[80];
printf("hex: %lld\n", (long long)fio_atol(&p));
++p; /* skip space */
printf("bin: %lld\n", (long long)fio_atol(&p));
++p;
printf("dec: %lld\n", (long long)fio_atol(&p));
fio_ltoa(buf, -255, 16);
printf("back to hex: %s\n", buf);
return 0;
}
```
------------------------------------------------------------
# CRC32
```c
#define FIO_CRC32
#include "fio-stl.h"
```
Fast CRC32 using the ITU-T V.42 / ISO 3309 / gzip polynomial (`0xEDB88320`). Useful for gzip, PNG, and anything else that expects this particular CRC32 flavor.
The implementation picks the best available path at compile time:
- ARM aarch64 with the CRC32 extension uses scalar CRC32 instructions, with a PMULL bulk-fold path when the crypto / PMULL feature is available.
- x86 / x64 with SSE4.2 and PCLMULQDQ uses a PCLMULQDQ bulk path and the software tail path.
- Other builds use the always-present slicing-by-16 software fallback.
**Note:** this is **not** CRC32-C (Castagnoli, polynomial `0x82F63B78`). The ARM scalar path uses the gzip-polynomial CRC32 instructions (`__crc32b`, `__crc32w`, `__crc32d`), not the Castagnoli variants.
### API Functions
#### `fio_crc32`
```c
SFUNC uint32_t fio_crc32(const void *data, size_t len, uint32_t initial_crc);
```
Computes the CRC32 of `len` bytes at `data`. Pass `0` for `initial_crc` to start fresh, or pass a previous result to continue an incremental checksum across multiple buffers.
**Returns:** the CRC32 checksum.
### Example — single buffer
```c
#define FIO_CRC32
#include "fio-stl.h"
int main(void) {
const char *msg = "Hello, World!";
uint32_t crc = fio_crc32(msg, strlen(msg), 0);
printf("CRC32: 0x%08X\n", crc);
return 0;
}
```
### Example — incremental
```c
uint32_t crc = 0;
crc = fio_crc32("Hello, ", 7, crc);
crc = fio_crc32("World!", 6, crc);
/* crc matches a single call over "Hello, World!" */
```
------------------------------------------------------------
# Glob Matching
```c
#define FIO_GLOB_MATCH
#include "fio-stl.h"
```
Binary glob matching for wildcard patterns. Bytes in, bytes out — no UTF-8 awareness, no allocation, just `*`, `?`, `[...]`, and escapes.
### API Functions
#### `fio_glob_match`
```c
SFUNC uint8_t fio_glob_match(fio_str_info_s pattern, fio_str_info_s string);
```
Returns `1` if `string` matches `pattern`, otherwise `0`.
### Supported Patterns
#### `*`
Matches any byte sequence, including an empty one.
#### `?`
Matches any single byte.
#### `[...]`
Matches any single byte inside the brackets. Ranges work with `-`, e.g. `[a-z]`. The first character may be `]` without closing the class. Use `\` to escape `]`, `-`, and `\` itself.
#### `[!...]` or `[^...]`
Inverted character class: matches any single byte **not** in the brackets.
#### `\`
Escapes the next character so it is matched literally.
### Example
```c
#define FIO_GLOB_MATCH
#define FIO_STR
#include "fio-stl.h"
int main(void) {
printf("%d\n", fio_glob_match(FIO_STR_INFO1("*.txt"),
FIO_STR_INFO1("notes.txt"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("log_????"),
FIO_STR_INFO1("log_2024"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("[!0-9]*"),
FIO_STR_INFO1("abc123"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("file\\*"),
FIO_STR_INFO1("file*"))); /* 1 */
return 0;
}
```
------------------------------------------------------------
# Index Mapped Array (iMap)
```c
#define FIO_IMAP_CORE
#include "fio-stl.h"
```
A macro-generated hash-map-backed array. Keeps insertion order in a dense array while the index map provides near-O(1) lookups. Mostly used internally by other STL modules; for public map needs see [`FIO_MAP_NAME`](https://facil.io/0.8.x/hash-maps-and-sets-module-210/).
**Note:** iMap does not manage object lifetimes. Clean up stored resources before calling destroy.
### Configuration Macros
#### `FIO_TYPEDEF_IMAP_REALLOC`
```c
#ifndef FIO_TYPEDEF_IMAP_REALLOC
#define FIO_TYPEDEF_IMAP_REALLOC FIO_MEM_REALLOC
#endif
```
Allocator used for iMap growth. Defaults to `FIO_MEM_REALLOC`.
#### `FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE`
```c
#ifndef FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE
#define FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE FIO_MEM_REALLOC_IS_SAFE
#endif
```
Set if the realloc zeroes new memory; otherwise iMap zeroes it explicitly.
#### `FIO_TYPEDEF_IMAP_FREE`
```c
#ifndef FIO_TYPEDEF_IMAP_FREE
#define FIO_TYPEDEF_IMAP_FREE FIO_MEM_FREE
#endif
```
Deallocator used by `array_name_destroy`.
### Helper Macros
#### `FIO_IMAP_ALWAYS_VALID`
```c
#define FIO_IMAP_ALWAYS_VALID(o) (1)
```
Validity helper that treats every object as valid.
#### `FIO_IMAP_VALID_NON_ZERO`
```c
#define FIO_IMAP_VALID_NON_ZERO(o) (!!((o)[0]))
```
Validity helper that treats an object as valid if its first element is non-zero.
#### `FIO_IMAP_ALWAYS_CMP_TRUE` / `FIO_IMAP_ALWAYS_CMP_FALSE`
```c
#define FIO_IMAP_ALWAYS_CMP_TRUE(a, b) (1)
#define FIO_IMAP_ALWAYS_CMP_FALSE(a, b) (0)
```
Comparison helpers that always return true or false.
#### `FIO_IMAP_SIMPLE_CMP`
```c
#define FIO_IMAP_SIMPLE_CMP(a, b) ((a)[0] == (b)[0])
```
Compares two objects by their first element.
#### `FIO_IMAP_EACH`
```c
#define FIO_IMAP_EACH(array_name, map_ptr, i) \
for (size_t i = 0; i < (map_ptr)->w; ++i) \
if (!FIO_NAME(array_name, is_valid)((map_ptr)->ary + i)) \
continue; \
else
```
Iterates valid elements in insertion order. Use it as a loop header:
```c
FIO_IMAP_EACH(my_map, &map, i) {
printf("%llu\n", (unsigned long long)map.ary[i].key);
}
```
### Type Definition Macro
#### `FIO_TYPEDEF_IMAP_ARRAY`
```c
#define FIO_TYPEDEF_IMAP_ARRAY(array_name, \
array_type, \
imap_type, \
hash_fn, \
cmp_fn, \
is_valid_fn)
```
Generates the iMap container type and functions prefixed with `array_name`. The callbacks take **pointers** to elements:
- `hash_fn(pobj)` returns an `imap_type` hash.
- `cmp_fn(a_ptr, b_ptr)` returns non-zero on match.
- `is_valid_fn(pobj)` returns non-zero if the element is valid.
Reserved index-map values: `0` means empty, `~0` means freed.
### Generated Types
#### `array_name_s`
```c
typedef struct {
array_type *ary;
imap_type count;
imap_type w;
uint32_t capa_bits;
} array_name_s;
```
- `ary` — dense array of elements.
- `count` — number of valid (non-removed) elements.
- `w` — next write position / logical size.
- `capa_bits` — log2 of capacity; actual capacity is `1 << capa_bits`.
#### `array_name_seeker_s`
```c
typedef struct {
imap_type pos;
imap_type ipos;
imap_type set_val;
} array_name_seeker_s;
```
- `pos` — array index, or `w` if not found.
- `ipos` — index-map slot, or `~0` if no room.
- `set_val` — value to write into the index map on insert.
### Generated Functions
#### `array_name_is_valid`
```c
FIO_IFUNC int array_name_is_valid(array_type *pobj);
```
Wraps the `is_valid_fn` supplied to the macro.
#### `array_name_capa`
```c
FIO_IFUNC size_t array_name_capa(array_name_s *a);
```
Returns `1 << a->capa_bits`, or `0` if not allocated.
#### `array_name_imap`
```c
FIO_IFUNC imap_type *array_name_imap(array_name_s *a);
```
Returns a pointer to the index map stored immediately after the array.
#### `array_name_destroy`
```c
FIO_IFUNC void array_name_destroy(array_name_s *a);
```
Frees the array + index map and zeros the container. Does not run destructors on elements.
#### `array_name_seek`
```c
FIO_SFUNC array_name_seeker_s array_name_seek(array_name_s *a,
array_type *pobj);
```
Finds an element or the place to insert it.
#### `array_name_reserve`
```c
FIO_IFUNC int array_name_reserve(array_name_s *a, imap_type min);
```
Grows the backing storage to at least `min` slots. Returns `0` on success, `-1` on failure.
#### `array_name_rehash`
```c
FIO_IFUNC int array_name_rehash(array_name_s *a);
```
Rebuilds the index map from the current array contents. Call after sorting or other position-changing operations. Returns `0` on success, `-1` on failure.
#### `array_name_set`
```c
FIO_IFUNC array_type *array_name_set(array_name_s *a,
array_type obj,
int overwrite);
```
Inserts or updates an element. If `overwrite` is zero and the key already exists, returns the existing element. Returns a pointer to the stored element or `NULL` on failure.
#### `array_name_get`
```c
FIO_IFUNC array_type *array_name_get(array_name_s *a, array_type obj);
```
Looks up an element. Returns a pointer to the match or `NULL`.
#### `array_name_remove`
```c
FIO_IFUNC int array_name_remove(array_name_s *a, array_type obj);
```
Zeros the matching element and marks its index-map slot as freed. Returns `0` on success, `-1` if not found.
### Internal Seeker Types
#### `fio___imapN_seeker_s`
```c
typedef struct {
uintN_t pos;
uintN_t ipos;
uintN_t set_val;
bool is_valid;
} fio___imapN_seeker_s;
```
Where `N` is `8`, `16`, `32`, or `64`. Low-level seeker used by other STL internals.
#### `fio___imapN_seek`
```c
FIO_SFUNC fio___imapN_seeker_s fio___imapN_seek(
void *ary,
uintN_t *imap,
const uintN_t capa_bits,
void *pobj,
uintN_t hash,
bool cmp_fn(void *arry, void *obj, uintN_t indx),
const size_t max_attempts);
```
Low-level seek through an index map. Generally used internally.
#### `fio___imapN_set`
```c
FIO_IFUNC void fio___imapN_set(uintN_t *imap,
uintN_t ipos,
uintN_t set_val);
```
Writes `set_val` into `imap[ipos]`.
### Example
```c
#define FIO_IMAP_CORE
#include "fio-stl.h"
typedef struct { uint64_t key; int value; } kv_s;
static uint64_t kv_hash(kv_s *p) { return fio_risky_num(p->key, 0); }
static int kv_cmp(kv_s *a, kv_s *b) { return a->key == b->key; }
static int kv_valid(kv_s *p) { return p->key != 0; }
FIO_TYPEDEF_IMAP_ARRAY(kv, kv_s, uint32_t, kv_hash, kv_cmp, kv_valid)
int main(void) {
kv_s map = {0};
kv_set(&map, (kv_s){.key = 1, .value = 100}, 1);
kv_set(&map, (kv_s){.key = 2, .value = 200}, 1);
kv_s *found = kv_get(&map, (kv_s){.key = 2});
if (found) printf("value: %d\n", found->value);
FIO_IMAP_EACH(kv, &map, i) {
printf("%llu -> %d\n",
(unsigned long long)map.ary[i].key, map.ary[i].value);
}
kv_remove(&map, (kv_s){.key = 2});
kv_destroy(&map);
return 0;
}
```
------------------------------------------------------------
# Multi-Precision Math
```c
#define FIO_MATH
#include "fio-stl.h"
```
Extra multi-precision operations on little-endian `uint64_t` arrays: division, shifts, two's-complement inverse, and bit-index queries. The core add/sub/mul helpers (`fio_math_add`, `fio_math_sub`, `fio_math_mul`, `fio_math_addc64`, etc.) and the wide union types (`fio_u128`, `fio_u256`, …) live in [`000 core.h`](https://facil.io/0.8.x/facil-io-stl-core/) and do not require `FIO_MATH`.
**Note:** this module assumes the CPU performs 64-bit multiply in constant time, which is not guaranteed on all hardware.
### API Functions
#### `fio_math_div`
```c
FIO_IFUNC void fio_math_div(uint64_t *dest,
uint64_t *reminder,
const uint64_t *a,
const uint64_t *b,
const size_t number_array_length);
```
Divides the `number_array_length`-word number `a` by `b`, storing the quotient in `dest` and the remainder in `reminder`. Either output may be `NULL` if you only need the other one.
**Note:** not constant time. Uses binary long division, which is simple but not the fastest algorithm on earth.
If `b` is zero, logs an error and fills both outputs with `0xFF`.
#### `fio_math_shr`
```c
FIO_IFUNC void fio_math_shr(uint64_t *dest,
uint64_t *n,
const size_t right_shift_bits,
size_t number_array_length);
```
Shifts `n` right by `right_shift_bits` and stores the result in `dest`.
#### `fio_math_shl`
```c
FIO_IFUNC void fio_math_shl(uint64_t *dest,
uint64_t *n,
const size_t left_shift_bits,
const size_t number_array_length);
```
Shifts `n` left by `left_shift_bits` and stores the result in `dest`.
#### `fio_math_inv`
```c
FIO_IFUNC void fio_math_inv(uint64_t *dest, uint64_t *n, size_t len);
```
Two's-complement negation of the `len`-word number `n`.
#### `fio_math_msb_index`
```c
FIO_MIFN size_t fio_math_msb_index(uint64_t *n, const size_t len);
```
Returns the zero-based index of the most significant set bit, or `(size_t)-1` if the number is zero. Useful for measuring bit length.
#### `fio_math_lsb_index`
```c
FIO_MIFN size_t fio_math_lsb_index(uint64_t *n, const size_t len);
```
Returns the zero-based index of the least significant set bit, or `(size_t)-1` if the number is zero.
### Example
```c
#define FIO_MATH
#include "fio-stl.h"
int main(void) {
uint64_t a[2] = {0, 1}; /* 2^64 */
uint64_t b[2] = {3, 0}; /* 3 */
uint64_t q[2] = {0};
uint64_t r[2] = {0};
fio_math_div(q, r, a, b, 2);
printf("quot: %016llX%016llX\n",
(unsigned long long)q[1], (unsigned long long)q[0]);
printf("rem: %016llX%016llX\n",
(unsigned long long)r[1], (unsigned long long)r[0]);
return 0;
}
```
------------------------------------------------------------
# Pseudo-Random and Hashing
```c
#define FIO_RAND
#include "fio-stl.h"
```
A non-cryptographic pseudo-random generator plus RiskyHash and Stable Hash. The PRNG is seeded from system jitter (`getrusage`, clocks) and hashed with RiskyHash. It is faster and more random than typical `rand()` implementations, but **do not use it for cryptography** — use `fio_rand_bytes_secure` for that.
RiskyHash is an ephemeral, non-cryptographic hash family that may change between releases. Stable Hash is frozen and safe for persistent data. RiskyHash 256/512 and HMAC variants are also provided.
### Pseudo-Random Functions
#### `fio_rand64`
```c
SFUNC uint64_t fio_rand64(void);
```
Returns 64 pseudo-random bits.
#### `fio_rand128`
```c
SFUNC fio_u128 fio_rand128(void);
```
Returns 128 pseudo-random bits as a `fio_u128`.
#### `fio_rand_bytes`
```c
SFUNC void fio_rand_bytes(void *target, size_t len);
```
Fills `target` with `len` pseudo-random bytes.
#### `fio_rand_bytes_secure`
```c
SFUNC int fio_rand_bytes_secure(void *target, size_t len);
```
Fills `target` with `len` cryptographically secure random bytes from the system CSPRNG: `arc4random_buf` on BSD/macOS, `BCryptGenRandom` on Windows, `getrandom` on Linux, and `/dev/urandom` as the fallback (also used when `getrandom` is unavailable or blocked). Returns `0` on success, `-1` on failure (no partial success; never falls back to the non-cryptographic PRNG). A `len` of `0` succeeds; a `NULL` `target` with `len > 0` fails.
**Windows linking**: MSVC links `bcrypt.lib` automatically (`#pragma comment`); MinGW / GNU-driver builds must link `-lbcrypt`.
Use this for keys, nonces, and anything security-sensitive.
#### `fio_rand_reseed`
```c
SFUNC void fio_rand_reseed(void);
```
Rotates the PRNG state with fresh entropy. Call after `fork` to avoid duplicated sequences.
### Risky / Stable Hash
#### `FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH`
```c
#define FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH 0
```
When set to `1`, `fio_risky_hash` redirects to `fio_stable_hash`.
#### `fio_risky_hash`
```c
SFUNC uint64_t fio_risky_hash(const void *buf, size_t len, uint64_t seed);
```
64-bit RiskyHash v.3. Ephemeral — hash values may change in future releases. Good for hash-map keys, not for storage.
#### `fio_risky_ptr`
```c
FIO_IFUNC uint64_t fio_risky_ptr(void *ptr);
```
Fast entropy mix for pointer values.
#### `fio_risky_num`
```c
FIO_IFUNC uint64_t fio_risky_num(uint64_t number, uint64_t seed);
```
Fast entropy mix for integer values.
#### `fio_stable_hash`
```c
SFUNC uint64_t fio_stable_hash(const void *data, size_t len, uint64_t seed);
```
64-bit Stable Hash. Frozen after 1.0; safe for on-disk or cross-system use.
#### `fio_stable_hash128`
```c
SFUNC void fio_stable_hash128(void *restrict dest,
const void *restrict data,
size_t len,
uint64_t seed);
```
Writes a 128-bit Stable Hash into `dest` (16 bytes).
#### `fio_risky256`
```c
SFUNC fio_u256 fio_risky256(const void *data, uint64_t len);
```
256-bit RiskyHash. Based on the A3 (Zero-Copy ILP) design: two 512-bit states, 128 bytes per iteration, multiply-fold with cross-lane mixing. Returns a `fio_u256`.
#### `fio_risky512`
```c
SFUNC fio_u512 fio_risky512(const void *data, uint64_t len);
```
512-bit RiskyHash. The first 256 bits are identical to `fio_risky256`; the rest come from an extra squeeze round.
#### `fio_risky256_hmac`
```c
SFUNC fio_u256 fio_risky256_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
RFC 2104 HMAC using `fio_risky256` as the hash, 64-byte block size. Keys longer than 64 bytes are hashed first. Securely zeros intermediates.
**Note:** the underlying hash is non-cryptographic; standard HMAC security proofs do not apply. Use Blake2 for cryptographic HMAC.
#### `fio_risky512_hmac`
```c
SFUNC fio_u512 fio_risky512_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Same construction as `fio_risky256_hmac`, but with `fio_risky512` and a 64-byte digest.
### Example
```c
#define FIO_RAND
#include "fio-stl.h"
int main(void) {
printf("rand64: %016llX\n", (unsigned long long)fio_rand64());
uint8_t key[32];
fio_rand_bytes_secure(key, sizeof(key));
uint64_t h = fio_stable_hash("hello", 5, 0);
printf("stable: %016llX\n", (unsigned long long)h);
fio_u256 r = fio_risky256("hello", 5);
printf("risky256: %016llX...\n",
(unsigned long long)r.u64[0]);
return 0;
}
```
------------------------------------------------------------
# Signal Monitoring
```c
#define FIO_SIGNAL
#include "fio-stl.h"
```
A small wrapper around OS signals. It sets a handler, records that the signal fired, and lets you call the real callback later from normal execution context — the safe pattern, since signal handlers are heavily restricted. POSIX and Windows are supported.
### Configuration Macros
#### `FIO_SIGNAL_MONITOR_MAX`
```c
#ifndef FIO_SIGNAL_MONITOR_MAX
#define FIO_SIGNAL_MONITOR_MAX 24
#endif
```
Maximum number of signals that can be monitored at once.
### Portable Signal Aliases
#### `FIO_SIGNAL_USER1` / `FIO_SIGNAL_USER2` / `FIO_SIGNAL_USER_UNREGISTERED`
```c
#if FIO_OS_POSIX
#define FIO_SIGNAL_USER1 SIGUSR1
#define FIO_SIGNAL_USER2 SIGUSR2
#define FIO_SIGNAL_USER_UNREGISTERED SIGUSR2
#elif FIO_OS_WIN
#define FIO_SIGNAL_USER1 SIGBREAK
#define FIO_SIGNAL_USER2 SIGABRT
#define FIO_SIGNAL_USER_UNREGISTERED SIGBREAK
#endif
```
Maps user signals across POSIX and Windows.
### Types
#### `fio_signal_monitor_args_s`
```c
typedef struct {
int sig;
void (*callback)(int sig, void *udata);
void *udata;
bool propagate;
bool immediate;
} fio_signal_monitor_args_s;
```
- `sig` — signal number to watch.
- `callback` — function to run; `NULL` ignores the signal.
- `udata` — opaque pointer passed to the callback.
- `propagate` — if true, call any previous handler after recording the signal.
- `immediate` — if true, run the callback inside the signal handler; otherwise defer to `fio_signal_review`.
### API Functions
#### `fio_signal_monitor`
```c
SFUNC int fio_signal_monitor(fio_signal_monitor_args_s args);
#define fio_signal_monitor(...) \
fio_signal_monitor((fio_signal_monitor_args_s){__VA_ARGS__})
```
Starts monitoring `sig`. Updating an existing monitor replaces its callback and `udata`. Returns `0` on success, `-1` on error.
**Note:** `immediate` callbacks must be async-signal-safe. Most code is not.
#### `fio_signal_review`
```c
SFUNC int fio_signal_review(void);
```
Calls deferred callbacks for any signals that fired since the last review. Returns the number of callbacks invoked.
#### `fio_signal_forget`
```c
SFUNC int fio_signal_forget(int sig);
```
Stops monitoring `sig` and restores the default handler. Returns `0` on success, `-1` on error.
### Example
```c
#define FIO_SIGNAL
#define FIO_LOG
#include "fio-stl.h"
static volatile int running = 1;
static void on_sigint(int sig, void *udata) {
(void)sig; (void)udata;
FIO_LOG_INFO("SIGINT received, shutting down...");
running = 0;
}
int main(void) {
fio_signal_monitor(.sig = SIGINT,
.callback = on_sigint,
.udata = NULL);
while (running) {
fio_signal_review();
/* do work */
}
fio_signal_forget(SIGINT);
return 0;
}
```
------------------------------------------------------------
# Sorting
```c
#define FIO_SORT_NAME num
#define FIO_SORT_TYPE size_t
#include "fio-stl.h"
```
A macro-generated quicksort + insert-sort combo. Define `FIO_SORT_NAME` and `FIO_SORT_TYPE` before including the header to get a set of sort functions named after your type. Include the header multiple times to sort different types.
### Configuration Macros
#### `FIO_SORT_NAME`
```c
#define FIO_SORT_NAME num
```
Prefix for generated function names: `num_sort`, `num_qsort`, `num_isort`.
#### `FIO_SORT_TYPE`
```c
#define FIO_SORT_TYPE size_t
```
The element type. **Required.**
#### `FIO_SORT_IS_BIGGER`
```c
#ifndef FIO_SORT_IS_BIGGER
#define FIO_SORT_IS_BIGGER(a, b) ((a) > (b))
#endif
```
Must evaluate to `1` if `a > b`, `0` otherwise. Override for custom ordering.
#### `FIO_SORT_SWAP`
```c
#ifndef FIO_SORT_SWAP
#define FIO_SORT_SWAP(a, b) \
do { \
FIO_SORT_TYPE tmp__ = (a); \
(a) = (b); \
(b) = tmp__; \
} while (0)
#endif
```
Swaps two elements. Override if your type needs a custom swap.
#### `FIO_SORT_THRESHOLD`
```c
#ifndef FIO_SORT_THRESHOLD
#define FIO_SORT_THRESHOLD 96
#endif
```
Partition size below which quicksort switches to insert-sort.
### API Functions
#### `FIO_SORT_NAME_sort`
```c
FIO_IFUNC void FIO_NAME(FIO_SORT_NAME, sort)(FIO_SORT_TYPE *array,
size_t count);
```
Sorts `array[0..count-1]` in ascending order. Currently dispatches to quicksort.
#### `FIO_SORT_NAME_qsort`
```c
SFUNC void FIO_NAME(FIO_SORT_NAME, qsort)(FIO_SORT_TYPE *array, size_t count);
```
Non-recursive quicksort with median-of-three pivot selection. Falls back to insert-sort for small partitions.
#### `FIO_SORT_NAME_isort`
```c
SFUNC void FIO_NAME(FIO_SORT_NAME, isort)(FIO_SORT_TYPE *array, size_t count);
```
Insert-sort for small or nearly-sorted arrays. O(n²), so avoid large inputs.
### Example
```c
#define FIO_SORT_NAME int
#define FIO_SORT_TYPE int
#include "fio-stl.h"
int main(void) {
int numbers[] = {5, 2, 8, 1, 9, 3, 7, 4, 6, 0};
size_t count = sizeof(numbers) / sizeof(numbers[0]);
int_sort(numbers, count);
for (size_t i = 0; i < count; ++i)
printf("%d ", numbers[i]);
printf("\n");
return 0;
}
```
------------------------------------------------------------
# Portable Threads
```c
#define FIO_THREADS
#include "fio-stl.h"
```
A thin, portable wrapper over POSIX pthreads and Windows threads. Provides thread creation, mutexes, condition variables, priorities, and a few process helpers. The wrappers intentionally return no thread exit value.
### Types
#### `fio_thread_t`
```c
/* POSIX */ typedef pthread_t fio_thread_t;
/* Windows */ typedef uintptr_t fio_thread_t;
```
Thread handle. On Windows this is the numeric thread ID; the handle is opened transiently only when needed.
#### `fio_thread_pid_t`
```c
/* POSIX */ typedef pid_t fio_thread_pid_t;
/* Windows */ typedef DWORD fio_thread_pid_t;
```
Process ID type.
#### `fio_thread_mutex_t`
```c
/* POSIX */ typedef pthread_mutex_t fio_thread_mutex_t;
/* Windows */ typedef SRWLOCK fio_thread_mutex_t;
```
Mutex type.
#### `fio_thread_cond_t`
```c
/* POSIX */ typedef pthread_cond_t fio_thread_cond_t;
/* Windows */ typedef CONDITION_VARIABLE fio_thread_cond_t;
```
Condition-variable type.
#### `fio_thread_priority_e`
```c
typedef enum {
FIO_THREAD_PRIORITY_ERROR = -1,
FIO_THREAD_PRIORITY_LOWEST = 0,
FIO_THREAD_PRIORITY_LOW,
FIO_THREAD_PRIORITY_NORMAL,
FIO_THREAD_PRIORITY_HIGH,
FIO_THREAD_PRIORITY_HIGHEST,
} fio_thread_priority_e;
```
Thread priority levels.
### Configuration Macros
#### `FIO_THREADS_BYO`
If defined, the thread creation/join/detach/current/yield functions are declared but not implemented — bring your own.
#### `FIO_THREADS_FORK_BYO`
If defined, the process helpers (`fio_thread_fork`, `getpid`, `kill`, `waitpid`) are declared but not implemented.
#### `FIO_THREADS_MUTEX_BYO`
If defined, the mutex functions are declared but not implemented.
#### `FIO_THREADS_COND_BYO`
If defined, the condition-variable functions are declared but not implemented.
#### `FIO_THREAD_MUTEX_INIT`
```c
/* POSIX */ #define FIO_THREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
/* Windows */ #define FIO_THREAD_MUTEX_INIT { 0 }
```
Static initializer for a mutex.
### Process Functions
#### `fio_thread_fork`
```c
FIO_IFUNC fio_thread_pid_t fio_thread_fork(void);
```
Behaves like POSIX `fork`. On Windows logs an error and returns `-1`.
#### `fio_thread_getpid`
```c
FIO_IFUNC fio_thread_pid_t fio_thread_getpid(void);
```
Behaves like POSIX `getpid`.
#### `fio_thread_kill`
```c
FIO_IFUNC int fio_thread_kill(fio_thread_pid_t pid, int sig);
```
Behaves like POSIX `kill`.
#### `fio_thread_waitpid`
```c
FIO_IFUNC int fio_thread_waitpid(fio_thread_pid_t pid,
int *stat_loc,
int options);
```
Behaves like POSIX `waitpid`.
### Thread Functions
#### `fio_thread_create`
```c
FIO_IFUNC int fio_thread_create(fio_thread_t *t,
void *(*fn)(void *),
void *arg);
```
Starts a new thread running `fn(arg)`. Returns `0` on success, `-1` on failure.
#### `fio_thread_join`
```c
FIO_IFUNC int fio_thread_join(fio_thread_t *t);
```
Waits for the thread to finish.
#### `fio_thread_detach`
```c
FIO_IFUNC int fio_thread_detach(fio_thread_t *t);
```
Detaches the thread so resources are reclaimed automatically.
#### `fio_thread_exit`
```c
FIO_IFUNC void fio_thread_exit(void);
```
Exits the current thread.
#### `fio_thread_equal`
```c
FIO_IFUNC int fio_thread_equal(fio_thread_t *a, fio_thread_t *b);
```
Returns non-zero if `a` and `b` refer to the same thread.
#### `fio_thread_current`
```c
FIO_IFUNC fio_thread_t fio_thread_current(void);
```
Returns a handle for the current thread.
#### `fio_thread_nid`
```c
FIO_IFUNC uintptr_t fio_thread_nid(void);
```
Returns a process-local numeral ID for the current thread. The value is stable
for the thread's lifetime and unique among live threads, but may be reused after
the thread exits. It is not guaranteed to be an OS-visible kernel thread ID.
#### `fio_thread_yield`
```c
FIO_IFUNC void fio_thread_yield(void);
```
Yields the CPU.
#### `fio_thread_priority`
```c
FIO_SFUNC fio_thread_priority_e fio_thread_priority(void);
```
Returns the current thread's priority, or `FIO_THREAD_PRIORITY_ERROR` on failure.
#### `fio_thread_priority_set`
```c
FIO_SFUNC int fio_thread_priority_set(fio_thread_priority_e pr);
```
Sets the current thread's priority. Returns `0` on success, `-1` on error.
### Mutex Functions
#### `fio_thread_mutex_init`
```c
FIO_IFUNC int fio_thread_mutex_init(fio_thread_mutex_t *m);
```
Initializes a mutex. You can also use `FIO_THREAD_MUTEX_INIT` for static initialization.
#### `fio_thread_mutex_lock`
```c
FIO_IFUNC int fio_thread_mutex_lock(fio_thread_mutex_t *m);
```
Locks `m`, blocking if necessary.
#### `fio_thread_mutex_trylock`
```c
FIO_IFUNC int fio_thread_mutex_trylock(fio_thread_mutex_t *m);
```
Tries to lock `m` without blocking. Returns `0` on success.
#### `fio_thread_mutex_unlock`
```c
FIO_IFUNC int fio_thread_mutex_unlock(fio_thread_mutex_t *m);
```
Unlocks `m`.
#### `fio_thread_mutex_destroy`
```c
FIO_IFUNC void fio_thread_mutex_destroy(fio_thread_mutex_t *m);
```
Destroys `m`.
### Condition Variable Functions
#### `fio_thread_cond_init`
```c
FIO_IFUNC int fio_thread_cond_init(fio_thread_cond_t *c);
```
Initializes a condition variable.
#### `fio_thread_cond_wait`
```c
FIO_IFUNC int fio_thread_cond_wait(fio_thread_cond_t *c,
fio_thread_mutex_t *m);
```
Waits on `c`. `m` must be locked; it is released while waiting and re-acquired on return.
#### `fio_thread_cond_timedwait`
```c
FIO_IFUNC int fio_thread_cond_timedwait(fio_thread_cond_t *c,
fio_thread_mutex_t *m,
size_t milliseconds);
```
Waits up to `milliseconds`. Returns `0` if signaled, non-zero on timeout/error.
#### `fio_thread_cond_signal`
```c
FIO_IFUNC int fio_thread_cond_signal(fio_thread_cond_t *c);
```
Wakes one waiter.
#### `fio_thread_cond_broadcast`
```c
FIO_IFUNC int fio_thread_cond_broadcast(fio_thread_cond_t *c);
```
Wakes all waiters.
#### `fio_thread_cond_destroy`
```c
FIO_IFUNC void fio_thread_cond_destroy(fio_thread_cond_t *c);
```
Destroys `c`.
### Example
```c
#define FIO_THREADS
#include "fio-stl.h"
static fio_thread_mutex_t lock = FIO_THREAD_MUTEX_INIT;
static int counter = 0;
static void *worker(void *arg) {
(void)arg;
for (int i = 0; i < 100000; ++i) {
fio_thread_mutex_lock(&lock);
++counter;
fio_thread_mutex_unlock(&lock);
}
return NULL;
}
int main(void) {
fio_thread_t t1, t2;
fio_thread_create(&t1, worker, NULL);
fio_thread_create(&t2, worker, NULL);
fio_thread_join(&t1);
fio_thread_join(&t2);
printf("counter: %d\n", counter);
fio_thread_mutex_destroy(&lock);
return 0;
}
```
------------------------------------------------------------
# URL / URI Parsing
```c
#define FIO_URL
#include "fio-stl.h"
```
Zero-copy, zero-allocation URI parser. Returns `fio_buf_info_s` slices pointing into the original string. No decoding is performed; pass the raw, encoded URI.
### Types
#### `fio_url_s`
```c
typedef struct {
fio_buf_info_s scheme;
fio_buf_info_s user;
fio_buf_info_s password;
fio_buf_info_s host;
fio_buf_info_s port;
fio_buf_info_s path;
fio_buf_info_s query;
fio_buf_info_s target;
} fio_url_s;
```
Parsed URL components. Empty components have `.buf == NULL` and `.len == 0`. Strings are **not** NUL terminated.
#### `fio_url_tls_info_s`
```c
typedef struct {
fio_buf_info_s key;
fio_buf_info_s cert;
fio_buf_info_s pass;
bool tls;
} fio_url_tls_info_s;
```
TLS material extracted from scheme or query parameters.
#### `fio_url_query_each_s`
```c
typedef struct {
fio_buf_info_s name;
fio_buf_info_s value;
fio_buf_info_s private___;
} fio_url_query_each_s;
```
Iterator state for `FIO_URL_QUERY_EACH`. Do not touch `private___`.
### API Functions
#### `fio_url_parse`
```c
SFUNC fio_url_s fio_url_parse(const char *url, size_t len);
```
Parses `url` of length `len` into components. Accepts many shapes:
- `/path?query#target`
- `host:port/path?query#target`
- `user:password@host:port/path?query#target`
- `scheme://user:password@host:port/path?query#target`
Invalid input produces unpredictable results — the parser does not validate.
**Note:** `file`, `unix`, and `priv` schemes are treated as file paths; the parser puts the whole remainder into `path` and clears host/port/user/password.
#### `fio_url_is_tls`
```c
SFUNC fio_url_tls_info_s fio_url_is_tls(fio_url_s u);
```
Detects TLS from scheme or query. Implicit TLS schemes include `wss`, `https`, `sses`, `ssse`, `tcps`, `stcp`, `tls`, `ssl`, `udps`, `sudp`. Explicit TLS is enabled by query keys `tls`, `key`, `cert`, `pass`, or `password`.
### Macros
#### `FIO_URL_QUERY_EACH`
```c
#define FIO_URL_QUERY_EACH(query_buf, i) \
for (fio_url_query_each_s i = fio_url_query_each_next( \
(fio_url_query_each_s){.private___ = (query_buf)}); \
i.name.buf; \
i = fio_url_query_each_next(i))
```
Iterates over query parameters. The iterator does not URL-decode names or values.
### Helper Functions
#### `fio_url_query_each_next`
```c
FIO_SFUNC fio_url_query_each_s fio_url_query_each_next(fio_url_query_each_s i);
```
Advances the query iterator. Usually called only through `FIO_URL_QUERY_EACH`.
### Example
```c
#define FIO_URL
#include "fio-stl.h"
int main(void) {
const char *s = "https://user:pass@example.com:8443/path?k=v#section";
fio_url_s u = fio_url_parse(s, strlen(s));
printf("scheme: %.*s\n", (int)u.scheme.len, u.scheme.buf);
printf("host: %.*s\n", (int)u.host.len, u.host.buf);
printf("port: %.*s\n", (int)u.port.len, u.port.buf);
printf("path: %.*s\n", (int)u.path.len, u.path.buf);
FIO_URL_QUERY_EACH(u.query, p) {
printf("%.*s = %.*s\n",
(int)p.name.len, p.name.buf,
(int)p.value.len, p.value.buf);
}
fio_url_tls_info_s tls = fio_url_is_tls(u);
printf("tls: %d\n", tls.tls);
return 0;
}
```
------------------------------------------------------------
# ML Entity Decoding
```c
#define FIO_ENTITY
#include "fio-stl.h"
```
Decodes a single Markup Language entity (`&name;`, `digits;`, `hex;`) into UTF-8. Handy when you need to unescape one entity at a time, for example while scanning HTML or XML text.
### API Functions
#### `fio_entity`
```c
SFUNC size_t fio_entity(char *dest, const char *src, size_t len);
```
Decodes the entity starting at `src` (length `len`). Writes the UTF-8 result to `dest`, which must be at least 8 bytes. A NUL terminator is written only if the result is shorter than 8 bytes.
**Returns:** the number of bytes written (excluding NUL), or `0` if `src` does not start with a valid entity.
Supported forms:
- Named: `&name;` — case-insensitive, ~40 common entities such as `<`, `&`, `©`, `—`.
- Decimal: `digits;`
- Hex: `hex;` or `hex;`
`` is replaced with the Unicode replacement character (`U+FFFD`). Code points above `0x10FFFF` are rejected.
### Example
```c
#define FIO_ENTITY
#include "fio-stl.h"
int main(void) {
const char *s = "© 2026 😀";
char buf[8];
size_t n = fio_entity(buf, s, strlen(s));
printf("decoded: %.*s\n", (int)n, buf);
return 0;
}
```
------------------------------------------------------------
# File Helpers
```c
#define FIO_FILES
#include "fio-stl.h"
```
POSIX-style file helpers: open, read, write, remove, folder creation, size, type, path safety, filename parsing, and temporary files. Nothing fancy — just the usual paperwork. Implemented in [`./004 files.h`](https://facil.io/0.8.x/api/004-files/).
### Configuration Macros
#### `FIO_FD_FIND_BLOCK`
```c
#ifndef FIO_FD_FIND_BLOCK
#define FIO_FD_FIND_BLOCK 4096
#endif
```
Stack buffer size used by `fio_fd_find_next` for each read cycle. Override before inclusion if you want larger or smaller scans.
#### `FIO_FILENAME_PATH_CAPA`
```c
#ifndef FIO_FILENAME_PATH_CAPA
#define FIO_FILENAME_PATH_CAPA (PATH_MAX | 4094)
#endif
```
Stack buffer capacity used for path manipulation (`"~/"` expansion in `fio_filename_open`, recursive `fio_filename_remove`, nested `fio_filename_make_path`). These functions allocate nothing — paths longer than this fail with `errno == ENAMETOOLONG`. The default guarantees at least ~4KB.
### Types
#### `fio_filename_s`
```c
typedef struct {
fio_buf_info_s folder;
fio_buf_info_s basename;
fio_buf_info_s ext;
} fio_filename_s;
```
Zero-copy result from `fio_filename_parse` / `fio_filename_parse2`. All members point into the original path string.
**Members:**
- `folder` - directory path including trailing separator, or `{NULL,0}` if none
- `basename` - file name without extension, or `{NULL,0}` if empty
- `ext` - extension without leading `.`, or `{NULL,0}` if none
### Macros
#### `FIO_FOLDER_SEPARATOR`
```c
#if FIO_OS_WIN
#define FIO_FOLDER_SEPARATOR '\\'
#else
#define FIO_FOLDER_SEPARATOR '/'
#endif
```
Native path separator. On Windows the parser also accepts `/`.
#### `fio_file_dup`
```c
#if FIO_OS_WIN
#define fio_file_dup(fd) _dup(fd)
#else
#define fio_file_dup(fd) dup(fd)
#endif
```
Duplicates a file descriptor.
#### `fio_filename_is_folder`
```c
#define fio_filename_is_folder(filename) \
(fio_filename_type((filename)) == S_IFDIR)
```
Returns non-zero if `filename` is a directory, `0` otherwise (including errors).
#### `FIO_FD_FIND_EOF`
```c
#define FIO_FD_FIND_EOF ((size_t)-1)
```
Sentinel returned by `fio_fd_find_next` when the token is not found before EOF.
### Opening and Creating Files
#### `fio_filename_open`
```c
SFUNC int fio_filename_open(const char *filename, int flags);
```
Opens `filename` with POSIX `open` semantics. If `filename` starts with `"~/"` (or `"~\\"` on Windows), it is resolved relative to `$HOME`.
**Parameters:**
- `filename` - path to open
- `flags` - `O_RDONLY`, `O_RDWR | O_CREAT | O_TRUNC`, etc.
**Returns:** file descriptor on success, `-1` on error.
**Note:** uses a fixed size stack buffer (zero allocations); over-long `"~/..."` paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
#### `fio_filename_tmp`
```c
SFUNC int fio_filename_tmp(void);
```
Creates a temporary file and returns its descriptor. Tries `TMPDIR`, `TMP`, `TEMP`, `P_tmpdir`, then the current directory. On Linux it attempts `O_TMPFILE` first; otherwise it builds a name prefixed with `facil_io_tmp_` and opens with `O_CREAT | O_EXCL`.
**Returns:** file descriptor on success, `-1` on error.
#### `fio_filename_overwrite`
```c
FIO_IFUNC int fio_filename_overwrite(const char *filename,
const void *buf,
size_t len);
```
Opens `filename` with `O_RDWR | O_CREAT | O_TRUNC`, writes `len` bytes from `buf`, and closes the file.
**Returns:** `0` on success, `-1` on error. On error the file state is undefined.
### Removing Files and Folders
#### `fio_filename_remove`
```c
typedef struct {
const char *path;
uint8_t folder;
uint8_t recursive;
} fio_filename_remove_args_s;
SFUNC int fio_filename_remove(fio_filename_remove_args_s args);
#define fio_filename_remove(...) \
fio_filename_remove((fio_filename_remove_args_s){__VA_ARGS__})
```
Removes the file / link or folder at `path`, abstracting OS differences (`unlink` / `rmdir` vs. `DeleteFileA` / `RemoveDirectoryA`).
**Parameters:**
- `path` - NUL-terminated path of the file / folder to remove
- `folder` - set to allow removal of an empty folder (like `rmdir`)
- `recursive` - set to remove a folder and all of its content (like `rm -r`); implies `folder`. If `path` isn't a folder, the flag is ignored and `path` is removed as a file / link
**Returns:** `0` on success, `-1` on error. Recursive removal stops on the first error (some content may remain). Uses a fixed size stack buffer (zero allocations); over-long paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
**Note:** links (symlinks / reparse points) are removed, never followed into.
**Example:**
```c
fio_filename_remove(.path = "old.log");
fio_filename_remove(.path = "empty_dir", .folder = 1);
fio_filename_remove(.path = "build_tree", .recursive = 1);
```
### Creating Folders
#### `fio_filename_make_path`
```c
typedef struct {
const char *path;
uint32_t mode;
} fio_filename_make_path_args_s;
SFUNC int fio_filename_make_path(fio_filename_make_path_args_s args);
#define fio_filename_make_path(...) \
fio_filename_make_path((fio_filename_make_path_args_s){__VA_ARGS__})
```
Creates the folder at `path`, including any missing parent folders (similar to `mkdir -p`). Abstracts OS differences (`mkdir` vs. `CreateDirectoryA`).
**Parameters:**
- `path` - NUL-terminated folder path to create; nested folders are created as needed, trailing separators are allowed
- `mode` - creation mode (POSIX only, ignored on Windows); zero defaults to `0755`
**Returns:** `0` on success, `-1` on error. An existing folder is NOT an error. A non-folder component along the way IS an error (`errno == ENOTDIR`). Uses a fixed size stack buffer (zero allocations); over-long paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
**Note:** root / drive / UNC `\\server\share` prefixes are never created, only the components after them.
**Example:**
```c
if (fio_filename_make_path(.path = "./build/out/artifacts"))
perror("mkdir -p");
```
### Reading and Writing
#### `fio_fd_write`
```c
FIO_IFUNC ssize_t fio_fd_write(int fd, const void *buf, size_t len);
```
Writes `len` bytes to `fd`, fragmenting the syscall into `write` blocks of up to `1 << 17` bytes. Retries on `EINTR`.
**Returns:** number of bytes written, or `-1` on error. If `fd == -1`, `buf == NULL`, or `len == 0`, returns `-1`.
**Note:** for non-blocking descriptors, check `errno` for `EAGAIN` / `EWOULDBLOCK`.
#### `fio_fd_read`
```c
FIO_IFUNC size_t fio_fd_read(int fd, void *buf, size_t len, off_t start_at);
```
Reads up to `len` bytes from `fd` starting at `start_at`. On POSIX systems with `pread`, the file offset is preserved; otherwise it seeks. Reads are fragmented to `1 << 27` byte chunks. Retries on `EINTR`.
**Parameters:**
- `fd` - file descriptor
- `buf` - destination buffer
- `len` - maximum bytes to read
- `start_at` - offset; negative values seek from end
**Returns:** bytes read, or `0` on error / EOF. Sets `errno = ENOENT` if `fd == -1`, `buf == NULL`, or `len == 0`.
### File Information
#### `fio_filename_size`
```c
FIO_IFUNC size_t fio_filename_size(const char *filename);
```
Returns file size in bytes, or `0` on error or empty file.
#### `fio_fd_size`
```c
FIO_IFUNC size_t fio_fd_size(int fd);
```
Returns file size for an open descriptor, or `0` on error or empty file.
#### `fio_filename_type`
```c
FIO_IFUNC size_t fio_filename_type(const char *filename);
```
Returns the file type bits (`st_mode & S_IFMT`), e.g. `S_IFREG`, `S_IFDIR`, or `0` on error.
#### `fio_fd_type`
```c
FIO_IFUNC size_t fio_fd_type(int fd);
```
Same as `fio_filename_type`, but for an open descriptor.
#### `fio_filename_stat`
```c
FIO_IFUNC int fio_filename_stat(const char *filename, struct stat *stat_buf);
```
Populates `stat_buf` via `stat`. Returns `0` on success, `-1` on error (including `filename == NULL` or `stat_buf == NULL`).
### Path Safety
#### `fio_filename_is_unsafe`
```c
SFUNC int fio_filename_is_unsafe(const char *path);
```
Returns `1` if `path` possibly folds backwards or contains double separators:
- leading `../`
- `/../` or trailing `/..`
- `//`
Uses the OS separator (`\` on Windows, `/` elsewhere). Returns `0` for `NULL` paths.
#### `fio_filename_is_unsafe_url`
```c
SFUNC int fio_filename_is_unsafe_url(const char *path);
```
Same check as `fio_filename_is_unsafe`, but always uses `/` as the separator. Use this for URL paths.
### Filename Parsing
#### `fio_filename_parse`
```c
SFUNC fio_filename_s fio_filename_parse(const char *filename);
```
Splits a NUL-terminated path into folder, basename, and extension without copying.
**Returns:** `fio_filename_s` with pointers into `filename`.
#### `fio_filename_parse2`
```c
SFUNC fio_filename_s fio_filename_parse2(const char *filename, size_t len);
```
Same as `fio_filename_parse`, but reads at most `len` bytes. Use when the input is not NUL-terminated.
### File Search
#### `fio_fd_find_next`
```c
SFUNC size_t fio_fd_find_next(int fd, char token, size_t start_at);
```
Scans `fd` for the next occurrence of `token` starting at `start_at`. Reads in `FIO_FD_FIND_BLOCK` byte chunks.
**Returns:** byte offset of the match, or `FIO_FD_FIND_EOF` if not found.
### Example
```c
#define FIO_FILES
#define FIO_LOG
#include "fio-stl.h"
int main(void) {
fio_filename_s parts = fio_filename_parse("/var/log/app.log");
printf("folder=%.*s base=%.*s ext=%.*s\n",
(int)parts.folder.len, parts.folder.buf,
(int)parts.basename.len, parts.basename.buf,
(int)parts.ext.len, parts.ext.buf);
const char *unsafe = "/var/www/../etc/passwd";
printf("unsafe? %s\n", fio_filename_is_unsafe(unsafe) ? "yes" : "no");
const char *msg = "hello, files";
if (fio_filename_overwrite("/tmp/fio_test.txt", msg, strlen(msg)) == 0) {
printf("wrote %zu bytes\n", strlen(msg));
}
size_t sz = fio_filename_size("/tmp/fio_test.txt");
printf("size = %zu\n", sz);
int tmp = fio_filename_tmp();
if (tmp != -1) {
fio_fd_write(tmp, "temp", 4);
close(tmp);
}
return 0;
}
```
------------------------------------------------------------
# HTTP/1.x Parser
```c
#define FIO_HTTP1_PARSER
#include FIO_INCLUDE_FILE
```
Small parser, sharp teeth. Define `FIO_HTTP1_PARSER` to add the static
HTTP/1.x request / response parser used by the HTTP layer. It performs no heap
allocations, stores only parser state, and reports parsed data through callbacks
implemented by the including translation unit.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the higher-level
[HTTP module header](./439 http.h), and the neighboring
[WebSocket parser header](./004 websocket parser.h).
---
## What Gets Added
`FIO_HTTP1_PARSER` exposes:
- `fio_http1_parser_s` — parser state.
- `FIO_HTTP1_PARSER_INIT` — zero-initializer / reset value.
- `fio_http1_parse` — incremental parser entry point.
- parser state helpers:
- `fio_http1_parser_is_empty`
- `fio_http1_parser_is_on_header`
- `fio_http1_parser_is_on_body`
- `fio_http1_expected`
- `fio_http1_parser_skip_body`
- `fio_http1_parser_skips_body`
- parse result / expected-body constants:
- `FIO_HTTP1_PARSER_ERROR`
- `FIO_HTTP1_EXPECTED_CHUNKED`
- required user callbacks named `fio_http1_on_*`.
The implementation also declares internal parsing stages named with
`fio_http1___...`; these are private implementation details.
---
## Parser State
### `fio_http1_parser_s`
```c
typedef struct fio_http1_parser_s fio_http1_parser_s;
struct fio_http1_parser_s {
int (*fn)(fio_http1_parser_s *, fio_buf_info_s *, void *);
size_t expected;
size_t flags;
};
```
The parser state is intentionally tiny: one function pointer for the current
state-machine stage, one `expected` byte counter / sentinel, and a `flags`
word for per-message state bits (e.g., an accepted `Expect: 100-continue`).
Parser state is never inferred from function pointer identity, so linker
identical-code-folding (MSVC / lld `/OPT:ICF`) cannot alter parsing behavior.
Treat all fields as opaque. Allocate the struct wherever it fits your lifetime
(stack, connection object, arena, etc.), initialize it with
`FIO_HTTP1_PARSER_INIT`, and use the helper functions to inspect state.
### `FIO_HTTP1_PARSER_INIT`
```c
#define FIO_HTTP1_PARSER_INIT ((fio_http1_parser_s){0})
```
Zero-initializes a parser:
```c
fio_http1_parser_s parser = FIO_HTTP1_PARSER_INIT;
```
The parser also resets itself to this empty state after a complete message is
reported with `fio_http1_on_complete`.
---
## Parsing API
### `fio_http1_parse`
```c
FIO_SFUNC size_t fio_http1_parse(fio_http1_parser_s *p,
fio_buf_info_s buf,
void *udata);
```
Parses as much HTTP/1.x data as currently possible and invokes callbacks as
fields are discovered.
- `p` is the parser state.
- `buf` is the current readable bytes.
- `udata` is passed unchanged to every callback.
Returns the number of bytes consumed from `buf`, or
`FIO_HTTP1_PARSER_ERROR` (`(size_t)-1`) on parse / callback error.
A successful return may consume fewer bytes than supplied. Any unconsumed bytes
belong to a later parse step or to the next HTTP message on a keep-alive
connection.
If the parser needs more bytes before it can make progress, it returns
successfully with the bytes consumed so far, which can be `0`.
### State Helpers
```c
FIO_IFUNC size_t fio_http1_parser_is_empty(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_is_on_header(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_is_on_body(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_expected(fio_http1_parser_s *p);
FIO_IFUNC void fio_http1_parser_skip_body(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_skips_body(fio_http1_parser_s *p);
FIO_IFUNC int fio_http1_version_is_legacy(fio_buf_info_s version);
```
- `fio_http1_parser_is_empty` returns non-zero when the parser is waiting for a
new request / response line.
- `fio_http1_parser_is_on_header` returns non-zero while reading regular
headers or chunked trailer headers.
- `fio_http1_parser_is_on_body` returns non-zero while reading a known-length
body or while the chunked parser is ready to read the next chunk frame. During
a split chunk payload, the internal chunk-read stage may report false even
though body bytes are still being drained.
- `fio_http1_expected` returns the parser's current expected byte count, returns
`FIO_HTTP1_EXPECTED_CHUNKED` after a chunked body is detected and before the
next chunk-size line is parsed, and returns `0` when the parser's internal
state marks the message as having no body. During chunked payload delivery,
it may expose the current chunk size / remaining chunk bytes.
- `fio_http1_parser_skip_body` marks the current message as having no body,
regardless of any `content-length` / `transfer-encoding` headers. Call it
from a parser callback before the headers end (normally `on_status`), e.g.,
when parsing the response to a `HEAD` request — the parser can't know the
request method. The mark is cleared when the message completes. See
*Messages Without a Body* below.
- `fio_http1_parser_skips_body` returns non-zero when the current message has
no body. It is known as soon as the first line is parsed (before any header
callback). Test it before reserving body space. See *Messages Without a Body*.
- `fio_http1_version_is_legacy` returns non-zero if a version string (as
passed to `fio_http1_on_version`) is exactly `HTTP/1.0` (case-sensitive).
HTTP/1.0 messages are not persistent without a `keep-alive` connection
option and never use chunked transfer coding (RFC 9112 §9.3, §6.1). Every
other version string gets HTTP/1.1 semantics. HTTP/0.9 is unsupported: its
version-less request line is rejected, and a `HTTP/0.9` version string is
treated as HTTP/1.1 (i.e., `host` is required).
### Constants
```c
#define FIO_HTTP1_PARSER_ERROR ((size_t)-1)
#define FIO_HTTP1_EXPECTED_CHUNKED ((size_t)(-2))
```
`FIO_HTTP1_PARSER_ERROR` is the error return value from `fio_http1_parse`.
After a parse error, close / discard the stream state rather than attempting to
recover the same parser instance.
`FIO_HTTP1_EXPECTED_CHUNKED` is the parser's sentinel after
`transfer-encoding: chunked` is accepted and before a chunk-size line is parsed.
Once chunk parsing starts, `fio_http1_expected` may instead report the current
chunk size or remaining chunk bytes.
The header also defines `FIO___HTTP1_BODY_NOT_ALLOWED` as an internal sentinel
for methods / states where a body should not be read. User code should rely on
`fio_http1_expected(p) == 0` instead of using that internal macro.
---
## Callback Contract
The parser declares these callbacks as `static` prototypes. The including
translation unit must define them.
For portable user code, every `int` callback should return only `0` to continue
or `-1` to reject the parse.
The parser's internal checks are not identical for every callback:
- `fio_http1_on_method`, `fio_http1_on_url`, `fio_http1_on_version`,
`fio_http1_on_status`, and `fio_http1_on_body_chunk` treat any non-zero return
as a parse error.
- `fio_http1_on_header` during normal headers and
`fio_http1_on_header_content_length` reject only an exact `-1` return.
- `fio_http1_on_header` during chunked trailers is passed through; negative
values become parse errors and positive values stop the current parse as
incomplete.
- `fio_http1_on_expect` is special: any non-zero return rejects the expectation,
resets the parser, and stops the current parse without calling
`fio_http1_on_complete`.
All `fio_buf_info_s` values point into the `buf` memory passed to
`fio_http1_parse`. They are not NUL-terminated unless the input happened to be.
Copy or retain the data before the callback returns if it must outlive the input
buffer.
> Important: the parser lowercases header names in-place. Feed it writable
> memory, not a string literal or read-only mapping.
### Completion
```c
static void fio_http1_on_complete(void *udata);
```
Called after the request / response line, headers, and any body have been fully
parsed. The parser is reset before this callback is invoked, so it is ready for
the next message on the same connection.
### Request Line Callbacks
```c
static int fio_http1_on_method(fio_buf_info_s method, void *udata);
static int fio_http1_on_url(fio_buf_info_s path, void *udata);
static int fio_http1_on_version(fio_buf_info_s version, void *udata);
```
For request lines, the parser calls the callbacks in this order:
1. `fio_http1_on_method`
2. `fio_http1_on_url`
3. `fio_http1_on_version`
The version slice is clamped to at most 14 bytes. `GET`, `HEAD`, and `OPTIONS`
are recognized case-insensitively and mark the parser as not expecting a body;
body-bearing headers for these methods conflict with that marker and are
rejected.
### Response Line Callbacks
```c
static int fio_http1_on_version(fio_buf_info_s version, void *udata);
static int fio_http1_on_status(size_t istatus,
fio_buf_info_s status,
void *udata);
```
For response lines, the parser calls `fio_http1_on_version` first and then
`fio_http1_on_status`.
`istatus` is parsed from the numeric status token. `status` is the remaining
status text slice after the numeric token. The version slice is clamped to at
most 14 bytes.
The parser decides whether the first line is a response by checking whether the
second token starts with a decimal digit.
Responses with a `1xx`, `204`, or `304` status are automatically marked as
having no body (see *Messages Without a Body*). The parser does not skip
interim `1xx` responses: each one completes as a separate message, and callers
decide whether to wait for the final response.
### Headers
```c
static int fio_http1_on_header(fio_buf_info_s name,
fio_buf_info_s value,
void *udata);
static int fio_http1_on_header_content_length(fio_buf_info_s name,
fio_buf_info_s value,
size_t content_length,
void *udata);
```
For ordinary headers, `fio_http1_on_header` receives:
- `name` lowercased in-place.
- `value` trimmed of leading and trailing spaces / tabs.
- an empty value as `{ .buf = NULL, .len = 0 }`.
Header names must contain a valid `:` separator and may not contain the
forbidden characters encoded by the parser. NUL bytes in header values are
rejected.
`content-length` is special:
- empty values are rejected;
- non-decimal / overflowing values are rejected;
- values colliding with internal sentinels are rejected;
- duplicate `content-length` headers must agree;
- conflicting `content-length` and final `transfer-encoding: chunked` are
rejected;
- the first non-zero accepted value calls `fio_http1_on_header_content_length`
instead of the generic header callback; a repeated matching value is accepted
without calling the content-length callback again.
A `content-length: 0` value marks the message as having no body and does not
call `fio_http1_on_header_content_length`.
`transfer-encoding` is also special. Its field lines form a single coding list
(RFC 9110 §5.3), and `chunked` (case-insensitive) must be the final coding,
applied exactly once (RFC 9112 §6.3, §7). Anything else can't be framed safely,
so it's rejected rather than reading the body as the next message (request
smuggling):
- the parser switches to chunked body decoding when a field line ends with the
`chunked` token;
- if the value is exactly `chunked`, no generic header callback is made;
- if other transfer-coding text appears before the final `chunked` token, the
final `chunked` token and adjacent separators are stripped before the
remaining value is passed to `fio_http1_on_header` (i.e.,
`gzip, chunked` is framed as chunked and forwards `transfer-encoding: gzip`;
decoding it is up to the parser user, since a `501` response is only a
SHOULD under RFC 9112 §6.1);
- a field line without a final `chunked` token (i.e., `gzip`) is forwarded
unchanged; a later field line may still end the list with `chunked`;
- rejected: a list that doesn't end with `chunked` once the headers end (i.e.,
`gzip`, `identity`, `chunked, gzip`, an empty value, or a `chunked` line
followed by any other `transfer-encoding` line), a repeated `chunked`
coding (`chunked, chunked`), malformed separators before the final
`chunked` token, a `content-length` header, and requests using `GET`,
`HEAD`, or `OPTIONS`.
Requests must carry exactly one `host` header, as required by RFC 9112 §3.2:
- an HTTP/1.1 request without a `host` header is rejected once the headers
end;
- a request with more than one `host` header line is rejected (for any
version);
- `HTTP/1.0` requests may omit `host` (see `fio_http1_version_is_legacy`);
- responses aren't affected. The `host` header itself is passed to
`fio_http1_on_header`.
`expect` is special when its value is exactly `100-continue`. Any other
`Expect` value is rejected.
### Messages Without a Body
The parser identifies bodyless messages from the first line, before any header
callback runs, and reports them through `fio_http1_parser_skips_body`:
- requests using `GET`, `HEAD`, or `OPTIONS` (a non-zero `content-length` on
these is still rejected, as described above);
- responses with a `1xx` / `204` / `304` status (RFC 9112 §6.3);
- messages marked with `fio_http1_parser_skip_body` (i.e., a `HEAD` response,
marked from `on_status`).
`fio_http1_parser_skips_body` is also true after a `content-length: 0` header.
A bodyless response completes at the empty line after its headers, and any
bytes that follow belong to the next message. Body framing is ignored:
- `content-length` is still validated and reported to
`fio_http1_on_header_content_length`, but it is informational. Callbacks must
test `fio_http1_parser_skips_body` before reserving space or enforcing body
size limits (a `HEAD` response may describe a large body that never arrives);
- `transfer-encoding` and `expect` are forwarded unchanged to
`fio_http1_on_header`.
### Expect: 100-continue
```c
static int fio_http1_on_expect(void *udata);
```
Called once, after headers, when an accepted `Expect: 100-continue` header
requires a post-header decision and a body may follow (a `Content-Length` body
or `transfer-encoding: chunked`). It is not called for messages that cannot
carry a body (e.g., `GET` / `HEAD` / `OPTIONS` or a zero `Content-Length`),
since there is nothing to continue (RFC 9110 §10.1.1).
Return `0` to continue into the body / completion flow. Return non-zero to reset
the parser and stop the current parse without calling `fio_http1_on_complete`.
### Body Chunks
```c
static int fio_http1_on_body_chunk(fio_buf_info_s chunk, void *udata);
```
Called with decoded body bytes.
For `Content-Length` bodies, callback chunks follow the supplied input chunks
and sum to the accepted content length.
For chunked bodies, framing bytes are removed before callback delivery. Large or
split HTTP chunks may be delivered through more than one callback if the input
arrives in smaller pieces.
---
## Parser Flow
```text
start line
├─ request -> method -> url -> version
└─ response -> version -> status
headers
├─ no body / body not allowed -> complete (incl. 1xx/204/304, skip_body)
├─ content-length body -> body chunks -> complete
└─ chunked body -> chunk chunks -> trailers -> complete
```
Details worth keeping in mind:
- Leading spaces, `\r`, and `\n` before the first line are skipped.
- First lines shorter than the parser's minimum accepted shape are rejected.
- NUL bytes in the first line are rejected.
- Header and first-line parsing waits for a newline before making progress.
- The parser accepts `\n` line endings and handles an optional preceding `\r`.
- Chunk size lines are hexadecimal, capped by the implementation, and do not
support chunk extensions.
- A zero-size chunk either completes immediately when followed by an empty line
or enters trailer parsing.
---
## Chunked Trailers
Allowed trailer headers are reported through `fio_http1_on_header` after the
body's terminating zero-size chunk.
The parser rejects the following trailer names:
- `authorization`
- `cache-control`
- `content-encoding`
- `content-length`
- `content-range`
- `content-type`
- `expect`
- `host`
- `max-forwards`
- `set-cookie`
- `te`
- `trailer`
- `transfer-encoding`
---
## Ownership and Lifetime
- The parser allocates no memory.
- The parser does not copy callback data.
- The parser mutates header names in the input buffer to lowercase.
- Callback slices are valid only while the input buffer remains valid and
unchanged.
- `udata` is never owned by the parser; it is simply forwarded.
- The parser state may live inside a connection object and be reused for
keep-alive messages. It resets automatically on complete messages.
- After `FIO_HTTP1_PARSER_ERROR`, discard the parser / connection state.
---
## Minimal Skeleton
```c
#define FIO_HTTP1_PARSER
#include FIO_INCLUDE_FILE
static int fio_http1_on_method(fio_buf_info_s method, void *udata) {
(void)method;
(void)udata;
return 0;
}
static int fio_http1_on_url(fio_buf_info_s path, void *udata) {
(void)path;
(void)udata;
return 0;
}
static int fio_http1_on_version(fio_buf_info_s version, void *udata) {
(void)version;
(void)udata;
return 0;
}
static int fio_http1_on_status(size_t status_code,
fio_buf_info_s status,
void *udata) {
(void)status_code;
(void)status;
(void)udata;
return 0;
}
static int fio_http1_on_header(fio_buf_info_s name,
fio_buf_info_s value,
void *udata) {
(void)name;
(void)value;
(void)udata;
return 0;
}
static int fio_http1_on_header_content_length(fio_buf_info_s name,
fio_buf_info_s value,
size_t content_length,
void *udata) {
(void)name;
(void)value;
(void)udata;
return content_length > (1UL << 20) ? -1 : 0;
}
static int fio_http1_on_expect(void *udata) {
(void)udata;
return 0;
}
static int fio_http1_on_body_chunk(fio_buf_info_s chunk, void *udata) {
(void)chunk;
(void)udata;
return 0;
}
static void fio_http1_on_complete(void *udata) {
(void)udata;
}
size_t parse_some_http(char *data, size_t len, void *udata) {
fio_http1_parser_s parser = FIO_HTTP1_PARSER_INIT;
return fio_http1_parse(&parser, FIO_BUF_INFO2(data, len), udata);
}
```
For real incremental parsing, keep `fio_http1_parser_s` with the connection and
preserve / retry unconsumed bytes when `fio_http1_parse` returns less than the
available buffer length.
# JSON Parser
```c
#define FIO_JSON
#include "fio-stl.h"
```
A non-strict, streaming, callback-based JSON parser. It does not build a tree for you; it calls your callbacks for each value and lets you decide what to construct. Implemented in [`./004 json.h`](https://facil.io/0.8.x/api/004-json/). Depends on `FIO_ATOL`, which is included automatically.
The parser tolerates trailing commas, comments (`//`, `/* */`, `#`), newlines inside strings, hex/octal/binary numbers, `NaN`, and `Infinity`.
### Configuration Macros
#### `FIO_JSON_MAX_DEPTH`
```c
#ifndef FIO_JSON_MAX_DEPTH
#define FIO_JSON_MAX_DEPTH 128
#endif
```
Maximum JSON nesting depth. Must be less than `65536`. Deeper input aborts with an error.
#### `FIO_JSON_USE_FIO_ATON`
```c
#ifndef FIO_JSON_USE_FIO_ATON
#define FIO_JSON_USE_FIO_ATON 0
#endif
```
Set to `1` to use `fio_aton` for number parsing instead of the built-in logic.
### Types
#### `fio_json_parser_callbacks_s`
```c
typedef struct {
void *(*on_null)(void *udata);
void *(*on_true)(void *udata);
void *(*on_false)(void *udata);
void *(*on_number)(void *udata, int64_t i);
void *(*on_float)(void *udata, double f);
void *(*on_string)(void *udata, const void *start, size_t len);
void *(*on_string_simple)(void *udata, const void *start, size_t len);
void *(*on_map)(void *udata, void *ctx, void *at);
void *(*on_array)(void *udata, void *ctx, void *at);
int (*map_push)(void *udata, void *ctx, void *key, void *value);
int (*array_push)(void *udata, void *ctx, void *value);
int (*array_finished)(void *udata, void *ctx);
int (*map_finished)(void *udata, void *ctx);
int (*is_array)(void *udata, void *ctx);
int (*is_map)(void *udata, void *ctx);
void (*free_unused_object)(void *udata, void *ctx);
void *(*on_error)(void *udata, void *ctx);
} fio_json_parser_callbacks_s;
```
Callback table for the parser. All callbacks receive `udata` as their first argument, so the same `static const` table can be shared across concurrent calls.
**Required callbacks:** `on_null`, `on_true`, `on_false`, `on_number`, `on_float`, `on_string` (or `on_string_simple`), `on_map`, `on_array`, `map_push`, `array_push`, `free_unused_object`.
**Optional callbacks:** `on_string_simple`, `array_finished`, `map_finished`, `is_array`, `is_map`, `on_error`. Missing optional callbacks default to no-ops. If only one of `on_string` / `on_string_simple` is provided, the other uses it as a fallback.
**Ownership:** callbacks that return `void *` transfer ownership to the parser. The parser passes objects to `map_push` / `array_push` or calls `free_unused_object` for discarded objects. `on_error` receives ownership of any partial result.
#### `fio_json_result_s`
```c
typedef struct {
void *ctx;
size_t stop_pos;
int err;
} fio_json_result_s;
```
Parse result.
**Members:**
- `ctx` - root object returned by callbacks, or `NULL`
- `stop_pos` - number of bytes consumed from the input
- `err` - non-zero if parsing stopped because of an error
### API Functions
#### `fio_json_parse`
```c
SFUNC fio_json_result_s fio_json_parse(fio_json_parser_callbacks_s *settings,
void *udata,
const char *json_string,
const size_t len);
```
Parses `json_string` up to `len` bytes. Stops as soon as a complete top-level value is consumed or an error occurs.
**Parameters:**
- `settings` - callback table
- `udata` - per-call user data threaded through every callback
- `json_string` - input buffer
- `len` - input length in bytes
**Returns:** `fio_json_result_s` with the root context, bytes consumed, and error flag.
**Note:** a UTF-8 BOM at the start of the buffer is skipped automatically.
**Guard-byte contract:** number and quote-less key scanning may read the byte at `json_string[len]` while deciding a token ended (see the `fio_atol*` guard-byte contract). Callers MUST keep that byte readable and non-numeric - pass a NUL-terminated string (any `fio_bstr` string qualifies) or append a non-numeric guard byte. Exact-size buffers with no readable byte past `len` violate the contract and will trigger AddressSanitizer.
#### `fio_json_parse_update`
```c
SFUNC fio_json_result_s fio_json_parse_update(fio_json_parser_callbacks_s *s,
void *udata,
void *ctx,
const char *start,
const size_t len);
```
Merges JSON data into an existing object (`ctx`). The input must be wrapped in an aggregate of the same type as `ctx`. Requires `is_array` and `is_map` callbacks.
**Parameters:**
- `s` - callback table
- `udata` - per-call user data
- `ctx` - existing object to update
- `start` - input buffer
- `len` - input length
**Returns:** parse result with the (possibly updated) context.
### Example
```c
#define FIO_JSON
#define FIO_LOG
#include "fio-stl.h"
typedef enum { T_NULL, T_TRUE, T_FALSE, T_NUM, T_STR, T_ARR, T_MAP } type_e;
typedef struct obj_s {
type_e type;
union {
int64_t i;
struct { char *buf; size_t len; } s;
struct { struct obj_s **items; size_t len; } a;
struct { struct obj_s **keys; struct obj_s **vals; size_t len; } m;
};
} obj_s;
static obj_s *mk(type_e t) {
obj_s *o = calloc(1, sizeof(*o));
o->type = t;
return o;
}
static void *on_null(void *u) { (void)u; return mk(T_NULL); }
static void *on_true(void *u) { (void)u; return mk(T_TRUE); }
static void *on_false(void *u) { (void)u; return mk(T_FALSE); }
static void *on_number(void *u, int64_t i) { (void)u; obj_s *o = mk(T_NUM); o->i = i; return o; }
static void *on_string(void *u, const void *s, size_t l) {
(void)u; obj_s *o = mk(T_STR); o->s.buf = malloc(l+1); memcpy(o->s.buf, s, l); o->s.buf[l] = 0; o->s.len = l; return o;
}
static void *on_array(void *u, void *c, void *a) { (void)u; (void)c; (void)a; return mk(T_ARR); }
static void *on_map(void *u, void *c, void *a) { (void)u; (void)c; (void)a; return mk(T_MAP); }
static int array_push(void *u, void *c, void *v) {
(void)u; obj_s *a = c; a->a.items = realloc(a->a.items, (a->a.len+1)*sizeof(*a->a.items)); a->a.items[a->a.len++] = v; return 0;
}
static int map_push(void *u, void *c, void *k, void *v) {
(void)u; obj_s *m = c;
m->m.keys = realloc(m->m.keys, (m->m.len+1)*sizeof(*m->m.keys));
m->m.vals = realloc(m->m.vals, (m->m.len+1)*sizeof(*m->m.vals));
m->m.keys[m->m.len] = k; m->m.vals[m->m.len] = v; m->m.len++; return 0;
}
static void free_obj(void *u, void *c) { (void)u; free(c); }
int main(void) {
static const fio_json_parser_callbacks_s cb = {
.on_null = on_null, .on_true = on_true, .on_false = on_false,
.on_number = on_number, .on_string = on_string, .on_string_simple = on_string,
.on_array = on_array, .on_map = on_map,
.array_push = array_push, .map_push = map_push,
.free_unused_object = free_obj,
};
const char *json = "{ \"n\": 42, \"s\": \"hi\", \"a\": [1, 2] }";
fio_json_result_s r = fio_json_parse(&cb, NULL, json, strlen(json));
if (r.err) {
FIO_LOG_ERROR("parse error at %zu", r.stop_pos);
return 1;
}
printf("parsed %zu bytes, root ctx %p\n", r.stop_pos, r.ctx);
return 0;
}
```
------------------------------------------------------------
# MIME Multipart Parser
```c
#define FIO_MULTIPART
#include "fio-stl.h"
```
A non-allocating, streaming, callback-based `multipart/form-data` parser. It finds parts, extracts `Content-Disposition` and `Content-Type`, then calls your callbacks for each field or file chunk. Implemented in [`./004 multipart.h`](https://facil.io/0.8.x/api/004-multipart/).
The parser does not decode transfer encodings or unescape headers — it just slices the original buffer into `fio_buf_info_s` ranges.
### Types
#### `fio_multipart_parser_callbacks_s`
```c
typedef struct {
void *(*on_field)(void *udata,
fio_buf_info_s name,
fio_buf_info_s value,
fio_buf_info_s content_type);
void *(*on_field_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s content_type);
int (*on_field_data)(void *udata, void *field_ctx, fio_buf_info_s data);
void (*on_field_end)(void *udata, void *field_ctx);
void *(*on_file_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s filename,
fio_buf_info_s content_type);
int (*on_file_data)(void *udata, void *file_ctx, fio_buf_info_s data);
void (*on_file_end)(void *udata, void *file_ctx);
void (*on_error)(void *udata);
} fio_multipart_parser_callbacks_s;
```
Callback table. All callbacks are optional; unset ones become no-ops.
**Members:**
- `on_field` - called for regular form fields (no `filename`). Ignored if `on_field_start` is provided.
- `on_field_start` - optional streaming field start. If set, `on_field` is ignored and `on_field_data` / `on_field_end` are used instead.
- `on_field_data` - called with a field data chunk. Return non-zero to abort.
- `on_field_end` - called when a streaming field ends.
- `on_file_start` - called when a file upload starts. Returns a context pointer for this file.
- `on_file_data` - called with a file data chunk. Return non-zero to abort.
- `on_file_end` - called when a file upload ends.
- `on_error` - called on parse error (optional).
#### `fio_multipart_result_s`
```c
typedef struct {
size_t consumed;
size_t field_count;
size_t file_count;
int err;
} fio_multipart_result_s;
```
Parse result.
**Members:**
- `consumed` - bytes consumed from the input buffer
- `field_count` - regular form fields parsed in this call
- `file_count` - file uploads parsed in this call
- `err` - `0` success, `-1` error, `-2` need more data
### API Functions
#### `fio_multipart_parse`
```c
SFUNC fio_multipart_result_s
fio_multipart_parse(const fio_multipart_parser_callbacks_s *callbacks,
void *udata,
fio_buf_info_s boundary,
const char *data,
size_t len);
```
Parses `data` as MIME multipart input. `boundary` is the boundary string **without** the leading `"--"` and must not exceed 250 bytes.
**Parameters:**
- `callbacks` - callback table (typically `static const`)
- `udata` - user data passed to every callback
- `boundary` - boundary value from the `Content-Type` header
- `data` - input buffer
- `len` - input length in bytes
**Returns:** parse result.
**Streaming behavior:**
- `err == 0` - parsing complete; closing boundary found.
- `err == -2` - need more data. Keep `data + result.consumed ... data + len` and append new data before calling again.
- `err == -1` - unrecoverable parse error or callback abort.
### Examples
#### Basic field and file parsing
```c
#define FIO_MULTIPART
#include "fio-stl.h"
static void *on_field(void *u, fio_buf_info_s n, fio_buf_info_s v, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("field %.*s = %.*s\n", (int)n.len, n.buf, (int)v.len, v.buf);
return NULL;
}
static void *on_file_start(void *u, fio_buf_info_s n, fio_buf_info_s fn, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("file %.*s / %.*s\n", (int)n.len, n.buf, (int)fn.len, fn.buf);
return NULL;
}
static int on_file_data(void *u, void *c, fio_buf_info_s d) {
(void)u; (void)c;
printf(" chunk %zu bytes\n", d.len);
return 0;
}
static void on_file_end(void *u, void *c) { (void)u; (void)c; }
int main(void) {
static const fio_multipart_parser_callbacks_s cb = {
.on_field = on_field,
.on_file_start = on_file_start,
.on_file_data = on_file_data,
.on_file_end = on_file_end,
};
const char *body =
"--B\r\n"
"Content-Disposition: form-data; name=\"x\"\r\n\r\n"
"hello\r\n"
"--B\r\n"
"Content-Disposition: form-data; name=\"f\"; filename=\"t.txt\"\r\n"
"Content-Type: text/plain\r\n\r\n"
"data\r\n"
"--B--\r\n";
fio_multipart_result_s r = fio_multipart_parse(
&cb, NULL, FIO_BUF_INFO2("B", 1), body, strlen(body));
printf("fields=%zu files=%zu err=%d\n", r.field_count, r.file_count, r.err);
return 0;
}
```
#### Streaming field callbacks
```c
static void *field_start(void *u, fio_buf_info_s n, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("field start %.*s\n", (int)n.len, n.buf);
return NULL;
}
static int field_data(void *u, void *c, fio_buf_info_s d) {
(void)u; (void)c;
printf(" field chunk %zu bytes\n", d.len);
return 0;
}
static void field_end(void *u, void *c) { (void)u; (void)c; }
static const fio_multipart_parser_callbacks_s cb_stream = {
.on_field_start = field_start,
.on_field_data = field_data,
.on_field_end = field_end,
};
```
------------------------------------------------------------
# RESP3 Parser
```c
#define FIO_RESP3
#include "fio-stl.h"
```
A streaming, callback-based [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) parser for Redis 6+ and compatible servers. Implemented in [`./004 resp3.h`](https://facil.io/0.8.x/api/004-resp3/). Depends on `FIO_ATOL`, included automatically.
The parser pushes values through a small internal stack, calling your callbacks for primitives, containers, and streaming strings. It does not allocate on its own.
### Configuration Macros
#### `FIO_RESP3_MAX_NESTING`
```c
#ifndef FIO_RESP3_MAX_NESTING
#define FIO_RESP3_MAX_NESTING 32
#endif
```
Maximum nesting depth. Valid range is 2 to 32,768. Determines the size of `fio_resp3_parser_s.stack`.
#### `FIO_RESP3_STREAM_THRESHOLD`
```c
#ifndef FIO_RESP3_STREAM_THRESHOLD
#define FIO_RESP3_STREAM_THRESHOLD 4096
#endif
```
Fixed-length blob strings (`$`, `!`, `=`) larger than this threshold are **streamed incrementally** when the callback table provides the streaming string callbacks (`on_start_string`, `on_string_write`, `on_string_done`): the parser starts the string as soon as its header is parsed and feeds data in chunks as it arrives (`result.consumed` advances per chunk), instead of waiting for the whole blob to be contiguous in the read window. This allows blobs larger than any consumer read buffer.
Blobs at or below the threshold keep the wait-for-contiguity behavior (a single zero-copy `on_string_write`, even across split reads). Callback tables without streaming callbacks are unaffected (they always wait for contiguity).
### RESP3 Type Constants
```c
#define FIO_RESP3_SIMPLE_STR '+' /* +\r\n */
#define FIO_RESP3_SIMPLE_ERR '-' /* -\r\n */
#define FIO_RESP3_NUMBER ':' /* :\r\n */
#define FIO_RESP3_NULL '_' /* _\r\n */
#define FIO_RESP3_DOUBLE ',' /* ,\r\n */
#define FIO_RESP3_BOOL '#' /* #t\r\n or #f\r\n */
#define FIO_RESP3_BIGNUM '(' /* (\r\n */
#define FIO_RESP3_BLOB_STR '$' /* $\r\n\r\n */
#define FIO_RESP3_BLOB_ERR '!' /* !\r\n\r\n */
#define FIO_RESP3_VERBATIM '=' /* =\r\n\r\n */
#define FIO_RESP3_ARRAY '*' /* *\r\n... */
#define FIO_RESP3_MAP '%' /* %\r\n... */
#define FIO_RESP3_SET '~' /* ~\r\n... */
#define FIO_RESP3_PUSH '>' /* >\r\n... */
#define FIO_RESP3_ATTR '|' /* |\r\n... */
#define FIO_RESP3_STREAM_CHUNK ';' /* ;\r\n\r\n */
#define FIO_RESP3_STREAM_END '.' /* .\r\n */
```
### Types
#### `fio_resp3_frame_s`
```c
typedef struct {
void *ctx;
void *key;
int64_t remaining;
uint8_t type;
uint8_t streaming;
uint8_t expecting_value;
uint8_t set_as_map;
} fio_resp3_frame_s;
```
Internal stack frame for a nested container. You do not need to touch this directly, but it is exposed in `fio_resp3_parser_s`.
#### `fio_resp3_parser_s`
```c
typedef struct {
void *udata;
uint32_t depth;
uint8_t error;
uint8_t streaming_string;
uint8_t streaming_string_type;
uint8_t streaming_blob_crlf;
void *streaming_string_ctx;
int64_t streaming_remaining;
fio_resp3_frame_s stack[FIO_RESP3_MAX_NESTING];
} fio_resp3_parser_s;
```
Parser state. Initialize with `{.udata = my_context}` before the first call and reuse it for continuation after partial parses.
**Members:**
- `udata` - user data passed to all callbacks
- `depth` - current nesting depth
- `error` - set to non-zero after a protocol error; the parser will refuse further input
- `streaming_string` - non-zero while a streamed string (`$?` chunked, or a fixed-length blob above `FIO_RESP3_STREAM_THRESHOLD`) is in progress
- `streaming_string_type` - type of the streaming string in progress
- `streaming_blob_crlf` - trailing CRLF bytes pending for a streamed fixed-length blob (0...2)
- `streaming_string_ctx` - context returned by `on_start_string`
- `streaming_remaining` - data bytes remaining for a streamed fixed-length blob (0 when inactive or in `$?` chunked mode)
- `stack` - nested container frames
#### `fio_resp3_callbacks_s`
```c
typedef struct {
/* primitives */
void *(*on_null)(void *udata);
void *(*on_bool)(void *udata, int is_true);
void *(*on_number)(void *udata, int64_t num);
void *(*on_double)(void *udata, double num);
void *(*on_bignum)(void *udata, const void *data, size_t len);
void *(*on_string)(void *udata, const void *data, size_t len, uint8_t type);
void *(*on_error)(void *udata, const void *data, size_t len, uint8_t type);
/* containers */
void *(*on_array)(void *udata, void *parent_ctx, int64_t len);
void *(*on_map)(void *udata, void *parent_ctx, int64_t len);
void *(*on_set)(void *udata, void *parent_ctx, int64_t len);
void *(*on_push)(void *udata, void *parent_ctx, int64_t len);
void *(*on_attr)(void *udata, void *parent_ctx, int64_t len);
/* push into containers */
int (*array_push)(void *udata, void *ctx, void *value);
int (*map_push)(void *udata, void *ctx, void *key, void *value);
int (*set_push)(void *udata, void *ctx, void *value);
int (*push_push)(void *udata, void *ctx, void *value);
int (*attr_push)(void *udata, void *ctx, void *key, void *value);
/* finalize containers */
void *(*array_done)(void *udata, void *ctx);
void *(*map_done)(void *udata, void *ctx);
void *(*set_done)(void *udata, void *ctx);
void *(*push_done)(void *udata, void *ctx);
void *(*attr_done)(void *udata, void *ctx);
/* errors */
void (*free_unused)(void *udata, void *obj);
void *(*on_error_protocol)(void *udata);
/* streaming strings */
void *(*on_start_string)(void *udata, size_t len, uint8_t type);
int (*on_string_write)(void *udata, void *ctx, const void *data, size_t len);
void *(*on_string_done)(void *udata, void *ctx, uint8_t type);
} fio_resp3_callbacks_s;
```
Callback table. Designed to be `static const`. Non-streaming callbacks left `NULL` are replaced with safe no-ops. Primitive no-ops return a non-NULL sentinel; container no-ops do the same; push no-ops return `0`; done no-ops return the context unchanged. The streaming-string callbacks (`on_start_string`, `on_string_write`, and `on_string_done`) are not filled with no-op fallbacks.
If `on_set`, `set_push`, and `set_done` are all `NULL` but map callbacks are provided, the parser treats RESP3 Sets as Maps, calling `map_push(ctx, value, value)` and `map_done`.
#### `fio_resp3_result_s`
```c
typedef struct {
void *obj;
size_t consumed;
int err;
} fio_resp3_result_s;
```
Parse result.
**Members:**
- `obj` - top-level object, or `NULL` while incomplete / on error
- `consumed` - bytes consumed from the buffer
- `err` - non-zero if a protocol error occurred
### API Functions
#### `fio_resp3_parse`
```c
SFUNC fio_resp3_result_s fio_resp3_parse(fio_resp3_parser_s *parser,
const fio_resp3_callbacks_s *callbacks,
const void *buf,
size_t len);
```
Parses as much RESP3 data as possible. State is preserved in `parser`, so you can call again with the remaining bytes after a partial read.
**Parameters:**
- `parser` - parser state
- `callbacks` - callback table (may be `NULL`; non-streaming callbacks become no-ops, while streaming-string callbacks remain unset)
- `buf` - input buffer
- `len` - input length
**Returns:** parse result.
**Note:** streamed strings (`$?`) require `on_start_string`, `on_string_write`, and `on_string_done` to be usable together. If `on_start_string` is missing or returns `NULL`, the parser sets `err` because it cannot buffer an unknown-length value. Once `on_start_string` returns a context, the parser enters streaming-string mode and later calls `on_string_write` and `on_string_done` directly, so those callbacks must also be supplied. For fixed-length blob, blob-error, and verbatim strings, `on_start_string` is optional; if it is missing or returns `NULL`, the parser falls back to `on_string` or `on_error`. If it returns a context for a fixed-length string, `on_string_write` and `on_string_done` are also called directly. Additionally, fixed-length blobs larger than `FIO_RESP3_STREAM_THRESHOLD` are always streamed incrementally when `on_start_string` is available (multiple `on_string_write` calls across parse calls possible), while smaller blobs complete in a single write once contiguous.
**Note:** top-level attributes (`|`) are delivered via callbacks but do not become the returned `obj`; parsing continues for the following reply.
### Example
```c
#define FIO_RESP3
#include "fio-stl.h"
static void *on_null(void *u) { (void)u; return (void *)1; }
static void *on_number(void *u, int64_t n) { (void)u; printf("num %lld\n", (long long)n); return (void *)1; }
static void *on_string(void *u, const void *d, size_t l, uint8_t t) {
(void)u; (void)t;
printf("str %.*s\n", (int)l, (const char *)d);
return (void *)1;
}
static void *on_array(void *u, void *c, int64_t l) { (void)u; (void)c; (void)l; return (void *)2; }
static int array_push(void *u, void *c, void *v) { (void)u; (void)c; (void)v; return 0; }
static void *array_done(void *u, void *c) { (void)u; return c; }
int main(void) {
static const fio_resp3_callbacks_s cb = {
.on_null = on_null, .on_number = on_number, .on_string = on_string,
.on_array = on_array, .array_push = array_push, .array_done = array_done,
};
const char *data = "*2\r\n:42\r\n$5\r\nhello\r\n";
fio_resp3_parser_s p = {0};
fio_resp3_result_s r = fio_resp3_parse(&p, &cb, data, strlen(data));
printf("consumed=%zu err=%d obj=%p\n", r.consumed, r.err, r.obj);
return 0;
}
```
------------------------------------------------------------
# Basic Socket Helpers
```c
#define FIO_SOCK
#include "fio-stl.h"
```
Portable socket helpers for POSIX and Windows: open, bind, connect, listen, wait, read, write, close, and a few sharp socket-shaped knives. Implemented in [`./004 sock.h`](https://facil.io/0.8.x/api/004-sock/).
`FIO_SOCK` depends on URL parsing internally and pulls in what it needs.
### Configuration Macros
#### `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`
```c
#ifndef FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT
#define FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT (1ULL << 24)
#endif
```
Default target used by `fio_sock_maximize_limits(0)`.
#### `FIO_SOCK_AVOID_UMASK`
```c
/* optional compile-time flag */
#define FIO_SOCK_AVOID_UMASK
```
If defined before including the implementation, Unix socket creation avoids the temporary `umask` call used when making public Unix sockets. This avoids `umask`'s process-global race, but may affect permissions on systems where `chmod` on Unix socket files is not enough.
Windows behaves as if this concern does not apply.
### Types and Flags
#### `fio_socket_i`
```c
#if FIO_OS_WIN
typedef SOCKET fio_socket_i;
#else
typedef int fio_socket_i;
#endif
```
Native socket handle type. Use this instead of `int` when writing portable code.
#### `FIO_SOCKET_INVALID`
```c
#if FIO_OS_WIN
#define FIO_SOCKET_INVALID INVALID_SOCKET
#else
#define FIO_SOCKET_INVALID (-1)
#endif
```
Invalid socket sentinel.
#### `FIO_SOCK_FD_ISVALID`
```c
#define FIO_SOCK_FD_ISVALID(fd) ((fio_socket_i)(fd) != FIO_SOCKET_INVALID)
```
Returns non-zero if `fd` is not the invalid sentinel.
#### `fio_sock_open_flags_e`
```c
typedef enum {
FIO_SOCK_SERVER = 0,
FIO_SOCK_CLIENT = 1,
FIO_SOCK_NONBLOCK = 2,
FIO_SOCK_TCP = 4,
FIO_SOCK_UDP = 8,
#ifdef AF_UNIX
FIO_SOCK_UNIX = 16,
FIO_SOCK_UNIX_PRIVATE = (16 | 32),
#else
#define FIO_SOCK_UNIX 0
#define FIO_SOCK_UNIX_PRIVATE 0
#endif
} fio_sock_open_flags_e;
```
Flags for `fio_sock_open`, `fio_sock_open2`, and `fio_sock_open_unix`.
**Values:**
- `FIO_SOCK_SERVER` - bind a local socket; TCP / stream sockets also call `listen`
- `FIO_SOCK_CLIENT` - connect a remote socket
- `FIO_SOCK_NONBLOCK` - set non-blocking mode
- `FIO_SOCK_TCP` - stream socket
- `FIO_SOCK_UDP` - datagram socket
- `FIO_SOCK_UNIX` - Unix domain socket where supported, otherwise `0`
- `FIO_SOCK_UNIX_PRIVATE` - Unix socket with private permissions where supported, otherwise `0`
`FIO_SOCK_TCP` and `FIO_SOCK_UDP` are exclusive. If neither is set, network sockets default to TCP.
### Opening Sockets
#### `fio_sock_open`
```c
FIO_IFUNC fio_socket_i fio_sock_open(const char *restrict address,
const char *restrict port,
uint16_t flags);
```
Creates a socket from an address / port pair and flags.
For `FIO_SOCK_UNIX`, `port` is ignored and `address` is treated as the Unix socket path. For server TCP sockets, `address == NULL` binds to all interfaces.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID` on error.
#### `fio_sock_open2`
```c
SFUNC fio_socket_i fio_sock_open2(const char *url, uint16_t flags);
```
Creates a socket from a URL-ish string. If no port is present, the URL scheme is used as the port / service name. Schemes such as `tcp`, `udp`, `http`, and `https` can also imply socket type when flags omit it.
Unix path URLs are detected when supported. A `priv` scheme selects `FIO_SOCK_UNIX_PRIVATE`; other path-only Unix forms select `FIO_SOCK_UNIX`.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID` on error.
#### `fio_sock_address_new`
```c
FIO_IFUNC struct addrinfo *fio_sock_address_new(const char *restrict address,
const char *restrict port,
int sock_type);
```
Resolves `address` / `port` into an `addrinfo` list for `sock_type`, usually `SOCK_STREAM` or `SOCK_DGRAM`.
Common service names are normalized before `getaddrinfo`: `ws` / `http` / `sse` to port `80`, and `wss` / `https` / `sses` to port `443`.
**Ownership:** free the result with `fio_sock_address_free`.
#### `fio_sock_address_free`
```c
FIO_IFUNC void fio_sock_address_free(struct addrinfo *a);
```
Frees an address list returned by `fio_sock_address_new`.
#### `fio_sock_open_local`
```c
SFUNC fio_socket_i fio_sock_open_local(struct addrinfo *addr, int nonblock);
```
Creates a network socket from an `addrinfo` list and binds it locally. `SO_REUSEADDR` is enabled. If `nonblock` is non-zero, non-blocking mode is requested before binding.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID`.
#### `fio_sock_open_remote`
```c
SFUNC fio_socket_i fio_sock_open_remote(struct addrinfo *addr, int nonblock);
```
Creates a network socket from an `addrinfo` list and connects it remotely. Non-blocking connect progress (`EINPROGRESS` / Windows equivalents) returns the socket.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID`.
#### `fio_sock_open_unix`
```c
SFUNC fio_socket_i fio_sock_open_unix(const char *address, uint16_t flags);
```
Creates a Unix domain socket where supported. Server sockets unlink an existing path before binding. Non-UDP server sockets call `listen`.
If Unix sockets are unsupported, the function asserts and returns `FIO_SOCKET_INVALID`.
### Portable Socket Operations
Use these wrappers when code should run on both POSIX and WinSock2.
#### `fio_sock_write`
```c
FIO_IFUNC ssize_t fio_sock_write(fio_socket_i fd, const void *buf, size_t len);
```
Acts like POSIX `write` on POSIX and uses `send` on Windows.
#### `fio_sock_read`
```c
FIO_IFUNC ssize_t fio_sock_read(fio_socket_i fd, void *buf, size_t len);
```
Acts like POSIX `read` on POSIX and uses `recv` on Windows.
#### `fio_sock_sendto`
```c
FIO_IFUNC ssize_t fio_sock_sendto(fio_socket_i fd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `sendto` wrapper.
#### `fio_sock_recvfrom`
```c
FIO_IFUNC ssize_t fio_sock_recvfrom(fio_socket_i fd,
void *buf,
size_t len,
int flags,
struct sockaddr *addr,
socklen_t *addrlen);
```
Portable `recvfrom` wrapper.
#### `fio_sock_close`
```c
FIO_IFUNC int fio_sock_close(fio_socket_i fd);
```
Portable close wrapper: `close` on POSIX, `closesocket` on Windows.
#### `fio_sock_accept`
```c
#if FIO_OS_WIN
IFUNC fio_socket_i fio_sock_accept(fio_socket_i s,
struct sockaddr *addr,
int *addrlen);
#else
#define fio_sock_accept(fd, addr, addrlen) accept(fd, addr, addrlen)
#endif
```
Portable accept wrapper.
#### `fio_sock_dup`
```c
FIO_IFUNC fio_socket_i fio_sock_dup(fio_socket_i fd);
```
Duplicates a socket handle. POSIX uses `dup` and sets `FD_CLOEXEC`; Windows uses `WSADuplicateSocket` and `WSASocket`.
#### `fio_sock_bind`
```c
FIO_IFUNC int fio_sock_bind(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `bind` wrapper.
#### `fio_sock_connect`
```c
FIO_IFUNC int fio_sock_connect(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `connect` wrapper.
#### `fio_sock_listen`
```c
FIO_IFUNC int fio_sock_listen(fio_socket_i fd, int backlog);
```
Portable `listen` wrapper.
#### `fio_sock_setsockopt`
```c
FIO_IFUNC int fio_sock_setsockopt(fio_socket_i fd,
int level,
int optname,
const void *optval,
socklen_t optlen);
```
Portable `setsockopt` wrapper. Pointer and length type differences are handled internally.
#### `fio_sock_socketpair`
```c
FIO_IFUNC int fio_sock_socketpair(fio_socket_i fds[2]);
```
Creates a connected socket pair. POSIX uses `socketpair(AF_UNIX, SOCK_STREAM, 0, fds)`. Windows creates a loopback TCP pair.
#### `fio_sock_pipe`
```c
FIO_IFUNC int fio_sock_pipe(fio_socket_i fds[2]);
```
Creates a pipe-like pair. POSIX uses `pipe`; Windows delegates to `fio_sock_socketpair` so the handles are usable with WinSock polling.
### Socket State and Waiting
#### `fio_sock_set_non_block`
```c
SFUNC int fio_sock_set_non_block(fio_socket_i fd);
```
Sets a socket to non-blocking mode.
**Returns:** `0` on success, `-1` on error.
#### `fio_sock_maximize_limits`
```c
SFUNC size_t fio_sock_maximize_limits(size_t maximum_limit);
```
Attempts to raise the open-file limit up to `maximum_limit`. Passing `0` uses `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`.
On Windows this returns the advisory cap because there is no `RLIMIT_NOFILE` equivalent for sockets.
#### `fio_sock_wait_io`
```c
SFUNC short fio_sock_wait_io(fio_socket_i fd, short events, int timeout);
```
Waits for `events` on one socket using `poll` / `WSAPoll`. `timeout` is in milliseconds; `0` returns immediately.
**Returns:** `0` on timeout, `-1` on error, or event bits such as `POLLIN`, `POLLOUT`, `POLLHUP`, and `POLLNVAL`.
#### `FIO_SOCK_WAIT_RW`
```c
#define FIO_SOCK_WAIT_RW(fd, timeout_) \
fio_sock_wait_io(fd, POLLIN | POLLOUT, timeout_)
```
Waits for read or write readiness.
#### `FIO_SOCK_WAIT_R`
```c
#define FIO_SOCK_WAIT_R(fd, timeout_) fio_sock_wait_io(fd, POLLIN, timeout_)
```
Waits for read readiness.
#### `FIO_SOCK_WAIT_W`
```c
#define FIO_SOCK_WAIT_W(fd, timeout_) fio_sock_wait_io(fd, POLLOUT, timeout_)
```
Waits for write readiness.
#### `FIO_SOCK_IS_OPEN`
```c
#ifdef POLLRDHUP
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, (POLLOUT | POLLRDHUP), 0) & \
(POLLRDHUP | POLLHUP | POLLNVAL)))
#else
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, POLLOUT, 0) & (POLLHUP | POLLNVAL)))
#endif
```
Best-effort test for whether a socket still appears open.
#### `fio_sock_peer_addr`
```c
SFUNC fio_buf_info_s fio_sock_peer_addr(fio_socket_i s);
```
Returns a numeric, human-readable peer address.
On error, returns `{NULL, 0}`. The returned buffer is internal static storage, limited to 63 bytes. Thread-safety is limited to 128 threads / calls.
### Windows Notes
On Windows, the module initializes WinSock in a constructor and stores direct function pointers loaded from `Ws2_32.dll`. Wrapper functions translate common WinSock errors into `errno` values.
The `fio___sock_wsa*` helpers are internal implementation details, not regular application API. Tiny dragons; leave them napping.
### Ownership and Thread-Safety
Socket handles are owned by the caller and should be closed with `fio_sock_close`. `fio_sock_address_new` results are owned by the caller and should be freed with `fio_sock_address_free`.
Socket operations are as thread-safe as the operating system calls they wrap. `fio_sock_peer_addr` uses shared static buffers and has the 128-call/thread caveat described above. Unix socket creation may touch process-global `umask` unless `FIO_SOCK_AVOID_UMASK` is defined.
### Example
```c
#define FIO_SOCK
#include "fio-stl.h"
#include
#include
int main(void) {
fio_socket_i fds[2];
char buf[8] = {0};
if (fio_sock_pipe(fds))
return 1;
fio_sock_write(fds[1], "ping", 4);
fio_sock_read(fds[0], buf, sizeof(buf) - 1);
printf("%s\n", buf);
fio_sock_close(fds[0]);
fio_sock_close(fds[1]);
return 0;
}
```
### TCP Server Sketch
```c
#define FIO_SOCK
#include "fio-stl.h"
int main(void) {
fio_socket_i s = fio_sock_open(NULL, "8080",
FIO_SOCK_SERVER | FIO_SOCK_TCP |
FIO_SOCK_NONBLOCK);
if (!FIO_SOCK_FD_ISVALID(s))
return 1;
if (FIO_SOCK_WAIT_R(s, 1000) & POLLIN) {
fio_socket_i c = fio_sock_accept(s, NULL, NULL);
if (FIO_SOCK_FD_ISVALID(c))
fio_sock_close(c);
}
fio_sock_close(s);
return 0;
}
```
---
# State Callbacks
```c
#define FIO_STATE
#include "fio-stl.h"
```
A small global event bus for lifecycle callbacks: initialize, fork, start, idle, shutdown, exit, and two user queues. Modules can listen without the caller knowing they exist. Sneaky, but polite. Implemented in [`./004 state callbacks.h`](https://facil.io/0.8.x/api/004-state-callbacks/).
Use this as a global `FIO_EXTERN` module when multiple translation units should share the same callback registry.
### Types
#### `fio_state_event_type_e`
```c
typedef enum {
FIO_CALL_ON_INITIALIZE,
FIO_CALL_PRE_START,
FIO_CALL_BEFORE_FORK,
FIO_CALL_IN_CHILD,
FIO_CALL_IN_MASTER,
FIO_CALL_AFTER_FORK,
FIO_CALL_ON_WORKER_THREAD_START,
FIO_CALL_ON_START,
FIO_CALL_RESERVED1,
FIO_CALL_RESERVED2,
FIO_CALL_ON_USER1,
FIO_CALL_ON_USER2,
FIO_CALL_ON_IDLE,
FIO_CALL_ON_USER1_REVERSE,
FIO_CALL_ON_USER2_REVERSE,
FIO_CALL_RESERVED1_REVERSED,
FIO_CALL_RESERVED2_REVERSED,
FIO_CALL_ON_SHUTDOWN,
FIO_CALL_ON_PARENT_CRUSH,
FIO_CALL_ON_CHILD_CRUSH,
FIO_CALL_ON_WORKER_THREAD_END,
FIO_CALL_ON_STOP,
FIO_CALL_AT_EXIT,
FIO_CALL_AFTER_EXIT,
FIO_CALL_NEVER
} fio_state_event_type_e;
```
Event queues. `FIO_CALL_NEVER` is the sentinel and should not be used as an event.
**Ordering:**
- events `<= FIO_CALL_ON_IDLE` run in registration order
- events `>= FIO_CALL_ON_SHUTDOWN` run in reverse registration order
`FIO_CALL_ON_USER1` / `FIO_CALL_ON_USER2` and their reverse variants are available for user code.
### Callback Management
#### `fio_state_callback_add`
```c
SFUNC void fio_state_callback_add(fio_state_event_type_e,
void (*func)(void *),
void *arg);
```
Adds `func(arg)` to an event queue. Adding the same function / argument pair replaces the existing map entry rather than creating a duplicate.
If `FIO_CALL_ON_INITIALIZE` already ran, adding another initialize callback calls it immediately.
#### `fio_state_callback_remove`
```c
SFUNC int fio_state_callback_remove(fio_state_event_type_e,
void (*func)(void *),
void *arg);
```
Removes `func(arg)` from an event queue.
**Returns:** the internal map removal result, or `-1` if the event is invalid.
#### `fio_state_callback_clear`
```c
SFUNC void fio_state_callback_clear(fio_state_event_type_e e);
```
Clears all callbacks for `e`. Invalid events are ignored.
This function is implemented in the header's implementation section; with `FIO_EXTERN`, expose the implementation in exactly one translation unit as usual.
#### `fio_state_callback_force`
```c
SFUNC void fio_state_callback_force(fio_state_event_type_e);
```
Runs the callbacks for an event as if the event happened.
During a forced event, the callback list is copied first. Additions and removals for the same event do not affect the current run.
Special behavior:
- `FIO_CALL_ON_INITIALIZE` runs only once and reserves internal maps
- `FIO_CALL_IN_CHILD` reinitializes internal locks and reseeds randomness
- `FIO_CALL_AFTER_FORK` reseeds randomness
### Debug Helpers
#### `fio_state_callback_print_state`
```c
FIO_IFUNC void fio_state_callback_print_state(void);
```
Prints callback counts and capacities for every event queue to `stderr`.
### Thread-Safety
Each event queue has its own lock. Adding, removing, clearing, and forcing callbacks are synchronized while touching the queue. Callback execution happens after the queue has been copied and unlocked.
### Example
```c
#define FIO_STATE
#include "fio-stl.h"
#include
static void hello(void *arg) {
printf("hello %s\n", (const char *)arg);
}
int main(void) {
fio_state_callback_add(FIO_CALL_ON_USER1, hello, "callbacks");
fio_state_callback_force(FIO_CALL_ON_USER1);
fio_state_callback_remove(FIO_CALL_ON_USER1, hello, "callbacks");
return 0;
}
```
---
# Time Helpers
```c
#define FIO_TIME
#include "fio-stl.h"
```
Wall-clock and monotonic time helpers, UTC calendar conversion, and a handful of date formatters. Tiny clock toolbox, no sundial required. Implemented in [`./004 time.h`](https://facil.io/0.8.x/api/004-time/).
### Collecting Time
#### `fio_time_real`
```c
FIO_IFUNC struct timespec fio_time_real(void);
```
Returns human / watch time using `CLOCK_REALTIME`. Good for timestamps, less good for measuring elapsed time because the system clock may jump.
#### `fio_time_mono`
```c
FIO_IFUNC struct timespec fio_time_mono(void);
```
Returns monotonic time using `CLOCK_MONOTONIC`. Prefer this for intervals and deadlines.
#### `fio_time_nano`
```c
FIO_IFUNC int64_t fio_time_nano(void);
```
Returns monotonic time in nanoseconds.
#### `fio_time_micro`
```c
FIO_IFUNC int64_t fio_time_micro(void);
```
Returns monotonic time in microseconds.
#### `fio_time_milli`
```c
FIO_IFUNC int64_t fio_time_milli(void);
```
Returns monotonic time in milliseconds.
### Conversion
#### `fio_time2milli`
```c
FIO_IFUNC int64_t fio_time2milli(struct timespec);
```
Converts a `struct timespec` to milliseconds.
#### `fio_time2micro`
```c
FIO_IFUNC int64_t fio_time2micro(struct timespec);
```
Converts a `struct timespec` to microseconds.
#### `fio_time2gm`
```c
SFUNC struct tm fio_time2gm(time_t time);
```
Converts `time` to a UTC `struct tm`. This is a faster, less localized alternative to `gmtime_r`.
#### `fio_gm2time`
```c
SFUNC time_t fio_gm2time(struct tm tm);
```
Converts a UTC `struct tm` to seconds. If `tm.tm_isdst > 0`, one hour is subtracted. On platforms with `tm_gmtoff`, that offset is added.
### Date Formatting
All formatting functions write to `target`, NUL-terminate the output, and return the number of bytes written before the NUL. Provide a buffer with enough space; `64` bytes is comfortably boring.
#### `fio_time2rfc7231`
```c
SFUNC size_t fio_time2rfc7231(char *target, time_t time);
```
Writes an RFC 7231 / HTTP date, usually 29 characters:
```text
Sun, 06 Nov 1994 08:49:37 GMT
```
#### `fio_time2rfc2109`
```c
SFUNC size_t fio_time2rfc2109(char *target, time_t time);
```
Writes an RFC 2109 cookie date, usually 31 characters:
```text
Sun, 06 Nov 1994 08:49:37 -0000
```
#### `fio_time2rfc2822`
```c
SFUNC size_t fio_time2rfc2822(char *target, time_t time);
```
Writes an RFC 2822 date, usually 28-29 characters:
```text
Sun, 6-Nov-1994 08:49:37 GMT
```
#### `fio_time2log`
```c
SFUNC size_t fio_time2log(char *target, time_t time);
```
Writes common log format:
```text
[DD/MMM/yyyy:hh:mm:ss +0000]
```
Usually needs 29 bytes including the NUL terminator.
#### `fio_time2iso`
```c
SFUNC size_t fio_time2iso(char *target, time_t time);
```
Writes an ISO-ish representation. The header comment says `YYYY-MM-DD HH:MM:SS`; the implementation writes a 3-letter month name:
```text
YYYY-MMM-DD HH:MM:SS
```
Usually needs 20 bytes including the NUL terminator.
### Arithmetic
#### `fio_time_add`
```c
FIO_IFUNC struct timespec fio_time_add(struct timespec t, struct timespec t2);
```
Adds two `struct timespec` values and normalizes the nanoseconds field.
#### `fio_time_add_milli`
```c
FIO_IFUNC struct timespec fio_time_add_milli(struct timespec t, int64_t milli);
```
Adds milliseconds to `t` and normalizes the result. The implementation splits milliseconds with a `1024`-millisecond approximation before normalization; cute, but be aware.
#### `fio_time_cmp`
```c
FIO_IFUNC int fio_time_cmp(struct timespec t1, struct timespec t2);
```
Compares two `struct timespec` values.
**Returns:** `-1` if `t1 < t2`, `0` if equal, `1` if `t1 > t2`.
### Example
```c
#define FIO_TIME
#include "fio-stl.h"
#include
#include
int main(void) {
struct timespec start = fio_time_mono();
struct timespec later = fio_time_add_milli(start, 500);
printf("now: %lld ms\n", (long long)fio_time_milli());
printf("later is after start: %d\n", fio_time_cmp(later, start) > 0);
char date[64];
fio_time2rfc7231(date, fio_time_real().tv_sec);
printf("http date: %s\n", date);
return 0;
}
```
---
# URL-Encoded Parser
```c
#define FIO_URL_ENCODED
#include "fio-stl.h"
```
A non-allocating parser for `application/x-www-form-urlencoded` data. It splits `name=value&...` pairs and leaves decoding to you. No copies, no drama. Implemented in [`./004 urlencoded.h`](https://facil.io/0.8.x/api/004-urlencoded/).
### Types
#### `fio_url_encoded_parser_callbacks_s`
```c
typedef struct {
void *(*on_pair)(void *udata, fio_buf_info_s name, fio_buf_info_s value);
void (*on_error)(void *udata);
} fio_url_encoded_parser_callbacks_s;
```
Parser callbacks. `udata` is passed to callbacks and updated from `on_pair`'s return value.
**Members:**
- `on_pair` - called for each pair. `name` and `value` point into the original input and are **not decoded**. Return non-NULL to continue, or `NULL` to stop.
- `on_error` - optional and currently unused; reserved because parsers enjoy having a rainy-day pocket.
If `callbacks` or a member is `NULL`, an internal no-op callback is used.
#### `fio_url_encoded_result_s`
```c
typedef struct {
size_t consumed;
size_t count;
int err;
} fio_url_encoded_result_s;
```
Parse result.
**Members:**
- `consumed` - number of input bytes consumed
- `count` - number of non-empty pairs reported
- `err` - non-zero when parsing stopped because `on_pair` returned `NULL`
### Parsing
#### `fio_url_encoded_parse`
```c
SFUNC fio_url_encoded_result_s
fio_url_encoded_parse(const fio_url_encoded_parser_callbacks_s *callbacks,
void *udata,
const char *data,
size_t len);
```
Parses `data[0..len)` as URL-encoded form data.
Rules from the implementation:
- pairs are separated by `&`
- the first `=` in a pair separates name and value
- `name=` is valid and has an empty value
- `name` is valid and has an empty value
- `=value` is valid and has an empty name
- empty pairs such as leading `&` or `&&` are skipped
- percent escapes are not decoded
Use a string helper such as `fio_string_write_url_dec` when you need decoded values.
**Ownership:** callback buffers borrow the original `data`; keep `data` alive while parsing and copy anything you need later.
**Thread-safety:** parsing uses only caller-provided state and stack locals.
### Example
```c
#define FIO_URL_ENCODED
#include "fio-stl.h"
#include
#include
static void *on_pair(void *udata, fio_buf_info_s name, fio_buf_info_s value) {
printf("%.*s = %.*s\n",
(int)name.len, name.buf,
(int)value.len, value.buf);
return udata;
}
int main(void) {
const char form[] = "name=Bo&empty=&encoded=a%20b&&solo";
static const fio_url_encoded_parser_callbacks_s cb = {
.on_pair = on_pair,
};
fio_url_encoded_result_s r =
fio_url_encoded_parse(&cb, (void *)1, form, strlen(form));
printf("pairs=%zu consumed=%zu err=%d\n", r.count, r.consumed, r.err);
return r.err;
}
```
---
# WebSocket Parser
```c
#define FIO_WEBSOCKET_PARSER
#include FIO_INCLUDE_FILE
```
Small RFC 6455 frame parser and writer. It keeps 24 bytes of parser state,
allocates nothing, unmasks incoming payload bytes in-place, and reports one
parse event at a time.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the neighboring
[HTTP/1.x parser](./004 http1 parser.md), the higher-level
[HTTP module](./439 http.md), and WebSocket compression support in
[DEFLATE / Gzip](./162 deflate.md).
---
## What Gets Added
`FIO_WEBSOCKET_PARSER` exposes:
- `fio_websocket_s` — incremental parser state.
- `fio_websocket_event_s` — one parse event returned by
`fio_websocket_parse`.
- `fio_websocket_init` / `fio_websocket_reset` — parser lifecycle helpers.
- `fio_websocket_parse` — incremental parser entry point.
- frame writer helpers for server and client data, ping, pong, and close
frames.
- WebSocket close code, event type, opcode, RSV, parser state, flag, and error
constants.
The implementation also defines helpers named with `fio___websocket...`; those
are private implementation details.
---
## Parser State
### `fio_websocket_s`
```c
typedef struct fio_websocket_s {
uint64_t frame_remaining;
uint32_t mask;
uint32_t frame_consumed;
uint16_t close_code;
uint8_t state;
uint8_t flags;
uint8_t flags2;
uint8_t reserved;
} fio_websocket_s;
```
The parser state is intentionally small and is asserted to fit in 24 bytes.
Allocate it wherever the connection state lives, initialize it before use, and
prefer the public helper macros below instead of editing fields by hand.
```c
FIO_WEBSOCKET_GET_FIN(p)
FIO_WEBSOCKET_GET_MASKED(p)
FIO_WEBSOCKET_GET_OPCODE(p)
FIO_WEBSOCKET_GET_MSG_OPCODE(p)
FIO_WEBSOCKET_GET_PAUSED(p)
FIO_WEBSOCKET_GET_MSG_RSV(p)
```
`GET_OPCODE` reports the current frame opcode. `GET_MSG_OPCODE` reports the
open message opcode (`FIO_WEBSOCKET_OP_TEXT`, `FIO_WEBSOCKET_OP_BINARY`, or
`0` when no message is open). `GET_MSG_RSV` reports the RSV bits from the
opening data frame in the same 3-bit format used by the writer API.
### Lifecycle
```c
FIO_IFUNC void fio_websocket_init(fio_websocket_s *p);
FIO_IFUNC void fio_websocket_reset(fio_websocket_s *p);
```
Both helpers clear the struct and set the parser to the header-reading state.
After a protocol error or after receiving a close frame, discard or reset the
parser before reusing it.
---
## Events
### `fio_websocket_event_s`
```c
typedef struct {
uint8_t type;
uint8_t opcode;
uint8_t is_text;
uint8_t rsv;
uint8_t is_first;
uint8_t is_last;
fio_buf_info_s payload;
uint16_t close_code;
} fio_websocket_event_s;
```
`payload` points into the input buffer passed to `fio_websocket_parse`. If the
incoming frame is masked, that memory is unmasked in-place before the event is
returned. Keep the input bytes writable and alive until the event payload is no
longer needed.
### Event Types
```c
FIO_WEBSOCKET_EV_NONE
FIO_WEBSOCKET_EV_DATA_CHUNK
FIO_WEBSOCKET_EV_CONTROL
FIO_WEBSOCKET_EV_MESSAGE_END
FIO_WEBSOCKET_EV_ERROR
```
The current parser reports message data with `FIO_WEBSOCKET_EV_DATA_CHUNK` and
marks message boundaries with `is_first` and `is_last`. Control frames are
reported whole with `FIO_WEBSOCKET_EV_CONTROL`. Protocol errors are reported as
`FIO_WEBSOCKET_EV_ERROR` when an event pointer is supplied.
`FIO_WEBSOCKET_EV_MESSAGE_END` is defined for API completeness, but the current
parse loop uses the `is_last` flag on data events instead of emitting a separate
message-end event.
### Event Fields
For data events:
- `opcode` is the current frame opcode. Continuation frames report
`FIO_WEBSOCKET_OP_CONT`.
- `is_text` is true when the open message started as a text message.
- `rsv` is copied from the opening data frame.
- `is_first` is true for the first chunk of the opening data frame.
- `is_last` is true for the final chunk of the message.
- `payload` is the available payload chunk, possibly length `0`.
For control events:
- `opcode` is `FIO_WEBSOCKET_OP_CLOSE`, `FIO_WEBSOCKET_OP_PING`, or
`FIO_WEBSOCKET_OP_PONG`.
- `payload` is the complete control payload, never a partial control frame.
- close frames set `close_code` to the on-wire close code, or
`FIO_WEBSOCKET_CLOSE_NO_STATUS` for an empty close payload.
---
## Parsing API
```c
FIO_SFUNC size_t fio_websocket_parse(fio_websocket_s *p,
fio_buf_info_s buf,
fio_websocket_event_s *ev);
```
Parses WebSocket bytes from `buf` and returns the number of bytes consumed, or
`FIO_WEBSOCKET_PARSE_ERROR` (`(size_t)-1`) on protocol error.
A successful call may consume fewer bytes than supplied. Feed the remaining
bytes to the next call, usually in a loop. If more bytes are needed before an
event can be produced, the function returns the bytes consumed so far, which can
be `0`, and leaves `ev->type` as `FIO_WEBSOCKET_EV_NONE`.
The parser is pure state plus the caller-provided buffer:
- no heap allocation;
- no callbacks;
- no retained payload pointers;
- no internal message accumulator;
- no control-frame buffering beyond waiting until a complete control payload is
available in the supplied input.
`ev` may be `NULL` if the caller only wants to advance or validate input, but
then event details and close/error codes must be read from parser state where
available.
### Parser Flow
```text
header
├─ incomplete header -> wait for more bytes
├─ control frame -> wait for full payload -> CONTROL event
└─ data / continuation -> unmask available bytes -> DATA_CHUNK event
└─ is_last marks message completion
```
The parser returns after at most one event. For fragmented messages, each data
frame may produce one or more data chunks depending on the input buffer splits.
Control frames may appear between fragmented data frames and are reported as
control events without closing the open message.
### Protocol Checks Performed
The parser rejects:
- unknown opcodes;
- fragmented control frames;
- continuation frames without an open message;
- nested text/binary messages while another data message is open;
- control payloads larger than 125 bytes;
- 64-bit lengths with the high bit set;
- frames larger than `FIO_WEBSOCKET_DEFAULT_MAX_FRAME`;
- close frames with a 1-byte payload;
- close frames carrying invalid on-wire close codes.
On rejection, the parser state becomes `FIO_WEBSOCKET_STATE_ERROR`,
`p->close_code` is set, and `fio_websocket_parse` returns
`FIO_WEBSOCKET_PARSE_ERROR`.
Caller policy still includes:
- enforcing client/server masking rules;
- interpreting RSV bits and applying extension transforms such as
`permessage-deflate`;
- enforcing total message size limits;
- validating UTF-8 for text messages;
- accumulating message chunks if whole-message delivery is desired.
---
## Frame Writers
Server writers produce unmasked frames. Client writers produce masked frames;
passing `mask == 0` asks the writer to generate a non-zero PRNG mask.
### Sizing
```c
FIO_IFUNC uint64_t fio_websocket_write_len(uint64_t payload_len, _Bool masked);
```
Returns the number of bytes required for one complete frame with the requested
payload length and masking mode. Allocate at least this many bytes before
calling a writer.
### Data Messages
```c
FIO_IFUNC uint64_t fio_websocket_write_message_server(void *target,
fio_buf_info_s msg,
_Bool is_text,
uint8_t rsv);
FIO_IFUNC uint64_t fio_websocket_write_message_client(void *target,
fio_buf_info_s msg,
_Bool is_text,
uint32_t mask,
uint8_t rsv);
```
Writes one complete FIN data message to `target` and returns the bytes written.
`is_text` selects text (`1`) or binary (`0`). `rsv` is the 3-bit RSV value;
usually pass `0`, or `FIO_WEBSOCKET_RSV1` for a compressed
`permessage-deflate` message after applying the extension transform.
### Ping / Pong
```c
FIO_IFUNC uint64_t fio_websocket_write_ping_server(void *target,
fio_buf_info_s payload);
FIO_IFUNC uint64_t fio_websocket_write_ping_client(void *target,
fio_buf_info_s payload,
uint32_t mask);
FIO_IFUNC uint64_t fio_websocket_write_pong_server(void *target,
fio_buf_info_s payload);
FIO_IFUNC uint64_t fio_websocket_write_pong_client(void *target,
fio_buf_info_s payload,
uint32_t mask);
```
Writes one FIN control frame. Keep ping and pong payloads at 125 bytes or less;
the writer does not add a separate policy check for that RFC limit.
### Close
```c
FIO_IFUNC uint64_t fio_websocket_write_close_server(void *target,
uint16_t code,
fio_buf_info_s reason);
FIO_IFUNC uint64_t fio_websocket_write_close_client(void *target,
uint16_t code,
fio_buf_info_s reason,
uint32_t mask);
```
Writes a close frame containing the 2-byte close code followed by the optional
reason. The reason is truncated so the whole close payload fits in the 125-byte
control-frame limit. Choose a close code that is valid to send on the wire.
---
## Constants
### Opcodes
```c
FIO_WEBSOCKET_OP_CONT
FIO_WEBSOCKET_OP_TEXT
FIO_WEBSOCKET_OP_BINARY
FIO_WEBSOCKET_OP_CLOSE
FIO_WEBSOCKET_OP_PING
FIO_WEBSOCKET_OP_PONG
```
### Close Codes
```c
FIO_WEBSOCKET_CLOSE_OK
FIO_WEBSOCKET_CLOSE_GOING_AWAY
FIO_WEBSOCKET_CLOSE_PROTOCOL_ERROR
FIO_WEBSOCKET_CLOSE_UNSUPPORTED_DATA
FIO_WEBSOCKET_CLOSE_NO_STATUS
FIO_WEBSOCKET_CLOSE_INVALID_PAYLOAD
FIO_WEBSOCKET_CLOSE_POLICY_VIOLATION
FIO_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG
FIO_WEBSOCKET_CLOSE_MANDATORY_EXT
FIO_WEBSOCKET_CLOSE_INTERNAL_ERROR
```
`FIO_WEBSOCKET_CLOSE_NO_STATUS` is synthesized for an empty received close
payload and must not be sent as an on-wire status code. The parser also accepts
valid registered wire codes such as 1012-1014 and application/library codes in
the 3000-4999 range.
### RSV Bits
```c
FIO_WEBSOCKET_RSV1
FIO_WEBSOCKET_RSV2
FIO_WEBSOCKET_RSV3
```
These are 3-bit values for the writer API and event `rsv` field. The writer
shifts them into byte-0 bits 4..6 on the wire.
### Parser States and Limits
```c
FIO_WEBSOCKET_STATE_HEADER
FIO_WEBSOCKET_STATE_PAYLOAD
FIO_WEBSOCKET_STATE_CLOSED
FIO_WEBSOCKET_STATE_ERROR
FIO_WEBSOCKET_DEFAULT_MAX_FRAME
FIO_WEBSOCKET_PARSE_ERROR
```
`FIO_WEBSOCKET_DEFAULT_MAX_FRAME` defaults to 1 GiB and may be overridden before
including the header. `FIO_WEBSOCKET_PARSE_ERROR` is the parse error sentinel.
The header also exposes flag bit masks used by the accessor macros:
`FIO_WEBSOCKET_FLAG_FIN`, `FIO_WEBSOCKET_FLAG_MASKED`,
`FIO_WEBSOCKET_FLAG_OPCODE_MASK`, `FIO_WEBSOCKET_FLAG_OPCODE_SHIFT`,
`FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK`, `FIO_WEBSOCKET_FLAG2_PAUSED`,
`FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK`, and
`FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT`.
---
## Minimal Parse Loop
```c
#define FIO_WEBSOCKET_PARSER
#include FIO_INCLUDE_FILE
typedef struct {
fio_websocket_s ws;
} connection_s;
void websocket_consume(connection_s *c, char *data, size_t len) {
fio_buf_info_s buf = FIO_BUF_INFO2(data, len);
while (buf.len) {
fio_websocket_event_s ev;
size_t n = fio_websocket_parse(&c->ws, buf, &ev);
if (n == FIO_WEBSOCKET_PARSE_ERROR) {
/* Send/record c->ws.close_code and close the connection. */
return;
}
buf.buf += n;
buf.len -= n;
if (!n && ev.type == FIO_WEBSOCKET_EV_NONE)
break; /* wait for more network bytes */
switch (ev.type) {
case FIO_WEBSOCKET_EV_DATA_CHUNK:
/* ev.payload is already unmasked and points into data. */
if (ev.is_first) {
/* start a message accumulator, if needed */
}
if (ev.payload.len) {
/* copy/process this chunk before reusing data */
}
if (ev.is_last) {
/* finish the message */
}
break;
case FIO_WEBSOCKET_EV_CONTROL:
if (ev.opcode == FIO_WEBSOCKET_OP_PING) {
char out[128 + 14];
uint64_t written = fio_websocket_write_pong_server(out, ev.payload);
(void)written; /* write out to the socket */
} else if (ev.opcode == FIO_WEBSOCKET_OP_CLOSE) {
/* Echo/close according to local policy. */
return;
}
break;
default:
break;
}
}
}
void websocket_open(connection_s *c) {
fio_websocket_init(&c->ws);
}
```
For real IO, preserve any unconsumed bytes and retry when more data arrives.
Copy payload bytes before recycling the read buffer or applying asynchronous
message handling.
---
## Minimal Write Example
```c
char out[14 + 1024];
fio_buf_info_s msg = FIO_BUF_INFO2("hello", 5);
uint64_t len = fio_websocket_write_message_server(out, msg, 1, 0);
/* write `len` bytes from out */
```
For client frames:
```c
uint64_t len = fio_websocket_write_message_client(out, msg, 1, 0, 0);
```
The fourth argument is an explicit mask; `0` lets the writer choose one.
---
## Ownership and Lifetime
- The parser owns only `fio_websocket_s` state supplied by the caller.
- The parser never allocates, frees, stores callbacks, or retains payload
pointers after returning.
- Input buffers passed to `fio_websocket_parse` must be writable because masked
payloads are modified in-place.
- Event payload slices are valid only while the input buffer remains valid and
unchanged.
- Writer targets must be large enough for `fio_websocket_write_len(payload.len,
masked)` bytes.
- Independent parser instances can be used concurrently by different threads;
synchronize shared buffers and connection state in the caller.
# CLI Helpers
```c
#define FIO_CLI
#include "fio-stl.h"
```
A small command-line parser with auto-generated help, typed flags, and unnamed-argument support. It is easier to set up than `getopt` and stays out of the way once you have your values.
Defining `FIO_CLI` also pulls in `FIO_ATOL`, `FIO_RAND`, and `FIO_IMAP` because the parser needs them internally.
**Note:** the module keeps a global data table. Parsing and setting values is **not** thread-safe. Reading values is safe after parsing is complete.
---
## Types
#### `fio_cli_arg_e`
```c
typedef enum {
FIO_CLI_ARG_STRING,
FIO_CLI_ARG_BOOL,
FIO_CLI_ARG_INT,
FIO_CLI_ARG_PRINT,
FIO_CLI_ARG_PRINT_LINE,
FIO_CLI_ARG_PRINT_HEADER,
} fio_cli_arg_e;
```
Argument type tags used internally and returned by `fio_cli_type`.
**Values:**
- `FIO_CLI_ARG_STRING` — accepts any string value.
- `FIO_CLI_ARG_BOOL` — a flag; presence means true.
- `FIO_CLI_ARG_INT` — validates the value as an integer.
- `FIO_CLI_ARG_PRINT` — indented help text line.
- `FIO_CLI_ARG_PRINT_LINE` — unindented help text line.
- `FIO_CLI_ARG_PRINT_HEADER` — section header in help output.
---
## Argument Definition Macros
Use these inside the `fio_cli_start` macro to describe each flag and help line.
#### `FIO_CLI_STRING`
```c
#define FIO_CLI_STRING(line) /* ... */
```
Declares a string argument.
```c
FIO_CLI_STRING("-o -output (stdout) output file path")
```
#### `FIO_CLI_INT`
```c
#define FIO_CLI_INT(line) /* ... */
```
Declares an integer argument. The parser validates the value with `fio_atol`.
```c
FIO_CLI_INT("-p -port (8080) the port number to use")
```
#### `FIO_CLI_BOOL`
```c
#define FIO_CLI_BOOL(line) /* ... */
```
Declares a boolean flag. Boolean flags cannot have default values. They can be chained, e.g. `-abc` is treated as `-a -b -c`.
```c
FIO_CLI_BOOL("-v -verbose enable verbose logging")
```
#### `FIO_CLI_PRINT`
```c
#define FIO_CLI_PRINT(line) /* ... */
```
Prints an extra indented help line after the previous argument.
```c
FIO_CLI_INT("-p -port (8080) the port number"),
FIO_CLI_PRINT("Set to 0 for Unix socket mode.")
```
#### `FIO_CLI_PRINT_LINE`
```c
#define FIO_CLI_PRINT_LINE(line) /* ... */
```
Prints a help line without indentation.
#### `FIO_CLI_PRINT_HEADER`
```c
#define FIO_CLI_PRINT_HEADER(line) /* ... */
```
Prints a section header in the help output.
```c
FIO_CLI_PRINT_HEADER("Network Options:")
```
---
## Initialization and Cleanup
#### `fio_cli_start`
```c
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
fio_cli_start((argc), \
(argv), \
(unnamed_min), \
(unnamed_max), \
(description), \
(fio___cli_line_s[]){__VA_ARGS__, {0}})
SFUNC void fio_cli_start FIO_NOOP(int argc,
char const *argv[],
int unnamed_min,
int unnamed_max,
char const *description,
fio___cli_line_s *arguments);
```
Parses `argv` into a dictionary of named and unnamed arguments, then prints help and exits on `-h`, `-?`, `-help`, or `--help`.
**Named Arguments:**
- `argc`, `argv` — values passed to `main`.
- `unnamed_min` — minimum required unnamed arguments. `-1` means no limit on unnamed arguments.
- `unnamed_max` — maximum allowed unnamed arguments. `-1` means unlimited.
- `description` — program description shown in help. The text `NAME` is replaced with `argv[0]`.
- `...` — argument description lines using the macros above.
Argument names must start with `-`. The first non-`-` word begins the description. Optional defaults go in parentheses: `(default)` or `("literal default")`.
Accepted input formats:
```text
app -t=1 -p3000 -a localhost
app -t 1 -p 3000 -a localhost
app --threads=1 --port=3000 --address=localhost
```
**Note:** this function is **not** thread-safe.
```c
#define FIO_CLI
#include "fio-stl.h"
int main(int argc, char const *argv[]) {
fio_cli_start(argc, argv, 0, -1,
"NAME - a small CLI example.\n",
FIO_CLI_PRINT_HEADER("Options:"),
FIO_CLI_STRING("-s -str (hello) a string"),
FIO_CLI_INT("-i -int (42) an integer"),
FIO_CLI_BOOL("-b -bool a boolean flag"),
FIO_CLI_PRINT("Boolean flags do not take values."));
fprintf(stderr, "string: %s\n", fio_cli_get("-s"));
fprintf(stderr, "int: %" PRId64 "\n", fio_cli_get_i("-i"));
fprintf(stderr, "bool: %d\n", (int)fio_cli_get_bool("-b"));
fio_cli_end();
return 0;
}
```
#### `fio_cli_end`
```c
SFUNC void fio_cli_end(void);
```
Frees the parsed CLI dictionary.
A destructor runs this automatically at program exit, but calling it earlier lets the memory be reused.
**Note:** this function is **not** thread-safe.
---
## Getting Values
#### `fio_cli_get`
```c
SFUNC char const *fio_cli_get(char const *name);
```
Returns the argument value as a NUL-terminated C string, or `NULL` if the argument was not provided. If `name` is `NULL`, returns the first unnamed argument.
#### `fio_cli_get_str`
```c
SFUNC fio_buf_info_s fio_cli_get_str(char const *name);
```
Returns the argument value as a `fio_buf_info_s` with length. If `name` is `NULL`, returns the first unnamed argument.
#### `fio_cli_get_i`
```c
SFUNC int64_t fio_cli_get_i(char const *name);
```
Returns the argument value parsed as an integer, or `0` if it was not provided. Integer parsing accepts decimal, hex, octal, and binary prefixes.
#### `fio_cli_get_bool`
```c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
```
Returns non-zero if the argument was provided, including by default.
#### `fio_cli_type`
```c
SFUNC fio_cli_arg_e fio_cli_type(char const *name);
```
Returns the declared argument type, or `FIO_CLI_ARG_NONE` if the name is unknown.
---
## Unnamed Arguments
#### `fio_cli_unnamed_count`
```c
SFUNC unsigned int fio_cli_unnamed_count(void);
```
Returns the number of unnamed arguments collected.
#### `fio_cli_unnamed`
```c
SFUNC char const *fio_cli_unnamed(unsigned int index);
```
Returns the unnamed argument at `index` as a NUL-terminated string, or `NULL` if `index` is out of bounds.
#### `fio_cli_unnamed_str`
```c
SFUNC fio_buf_info_s fio_cli_unnamed_str(unsigned int index);
```
Returns the unnamed argument at `index` as a `fio_buf_info_s`, or an empty `fio_buf_info_s` if out of bounds.
---
## Setting Values
#### `fio_cli_set`
```c
SFUNC void fio_cli_set(char const *name, char const *value);
```
Sets or overwrites a named argument's value. If `name` is `NULL`, appends `value` as a new unnamed argument.
**Note:** this function is **not** thread-safe.
#### `fio_cli_set_i`
```c
SFUNC void fio_cli_set_i(char const *name, int64_t i);
```
Sets a named argument to a base-10 string representation of `i`.
**Note:** this function is **not** thread-safe.
#### `fio_cli_set_unnamed`
```c
SFUNC unsigned int fio_cli_set_unnamed(unsigned int index, const char *value);
```
Sets or appends an unnamed argument. If `index` is past the end, the value is appended. Returns the stored index, or `(unsigned int)-1` if `value` is `NULL` or empty.
**Note:** this function is **not** thread-safe.
---
## Iteration
#### `fio_cli_each`
```c
SFUNC size_t fio_cli_each(int (*task)(fio_buf_info_s name,
fio_buf_info_s value,
fio_cli_arg_e arg_type,
void *udata),
void *udata);
```
Calls `task` for every argument that has a value, returning the number of calls made. If `task` returns non-zero, iteration stops. For unnamed arguments, `name.buf` is `NULL` and `name.len` is `0`.
------------------------------------------------------------
# Memory Allocator
```c
#define FIO_MEMORY_NAME fio
#include "fio-stl.h"
```
A concurrent, per-arena memory allocator that groups objects with similar lifetimes. It tries to keep related allocations in the same chunk so cleanup is cheap and locality is decent. Define `FIO_MEMORY_DISABLE` to route everything to the system `malloc`/`free` instead.
The prefix `fio` comes from `FIO_MEMORY_NAME`. All public function names are built with `FIO_NAME(FIO_MEMORY_NAME, ...)`, so a custom prefix produces `my_malloc`, `my_free`, and so on.
The module also sets the temporary allocator macros (`FIO_MEM_REALLOC_`, `FIO_MEM_FREE_`, `FIO_MEM_REALLOC_IS_SAFE_`, `FIO_MEM_ALIGNMENT_SIZE_`) for any facil.io type defined in the same `include` scope.
**Note:** `FIO_MALLOC` is a shortcut that defines this allocator with prefix `fio` and general-purpose tuning.
---
## Core API
#### `fio_malloc`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, malloc)(size_t size);
```
Allocates `size` bytes aligned to `FIO_MEMORY_ALIGN_SIZE`. Memory is zeroed if `FIO_MEMORY_INITIALIZE_ALLOCATIONS` is enabled. Allocations above the arena limit use big-block mode or `mmap`.
**Parameters:**
- `size` — bytes to allocate.
**Returns:** pointer to allocated memory, or `NULL` on failure.
#### `fio_calloc`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc)(size_t size_per_unit,
size_t unit_count);
```
Equivalent to `fio_malloc(size_per_unit * unit_count)`.
#### `fio_realloc`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc)(void *ptr,
size_t new_size);
```
Reallocates `ptr` to `new_size`. Copy-free expansion is only attempted for large allocations. Prefer `fio_realloc2` for predictable zeroing.
**Parameters:**
- `ptr` — existing allocation, or `NULL`.
- `new_size` — requested new size.
**Returns:** pointer to the new block, or `NULL` on failure.
#### `fio_realloc2`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc2)(void *ptr,
size_t new_size,
size_t copy_len);
```
Reallocates `ptr` to `new_size`, copying at most `copy_len` bytes. Memory beyond `copy_len` is zeroed when `FIO_MEMORY_INITIALIZE_ALLOCATIONS` is enabled.
**Parameters:**
- `ptr` — existing allocation, or `NULL`.
- `new_size` — requested new size.
- `copy_len` — maximum bytes to copy from the old block.
**Returns:** pointer to the new block, or `NULL` on failure.
#### `fio_free`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, free)(void *ptr);
```
Frees memory allocated by this allocator. Freeing a pointer from a different allocator, or after cleanup, is undefined behavior.
#### `fio_mmap`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, mmap)(size_t size);
```
Allocates directly from the system with `mmap` semantics. Use this for large, long-lived objects. The allocation is slower but avoids arena bookkeeping.
**Parameters:**
- `size` — bytes to allocate.
**Returns:** pointer to allocated memory, or `NULL` on failure.
#### `fio_malloc_after_fork`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_after_fork)(void);
```
Reinitializes allocator locks after `fork` in the child process. Best effort only: forking a multi-threaded process is unsafe. Prefer calling `fio_state_callback_force(FIO_CALL_IN_CHILD);` to reset all registered allocators.
#### `fio_realloc_is_safe`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, realloc_is_safe)(void);
```
Returns non-zero if the allocator zeroes allocations, which makes `fio_realloc2` safe for the uncopied tail.
---
## Aligned Allocation API
The aligned API requests a minimum pointer alignment. `fio_malloc_aligned` and `fio_calloc_aligned` are thin wrappers around `fio_realloc_aligned`, which is the single entry point.
#### `fio_realloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME,
realloc_aligned)(void *ptr,
size_t new_size,
size_t copy_len,
size_t alignment);
```
Reallocates `ptr` to `new_size` (copying at most `copy_len` bytes, like `fio_realloc2`), guaranteeing the returned pointer is aligned to at least `alignment`.
The `alignment` argument is normalized:
- `0` selects the allocator default (`FIO_MEMORY_ALIGN_SIZE`).
- Non power-of-2 values are rounded **down** to the nearest power of 2.
- The effective alignment is the maximum of the (rounded) request, the current alignment of `ptr`, and `FIO_MEMORY_ALIGN_SIZE` — alignment never regresses across a realloc.
- Values above `FIO_MEMORY_SYS_ALLOCATION_SIZE` fail: returns `NULL` and sets `errno` to `EINVAL` (debug builds also trip `FIO_ASSERT_DEBUG`).
In-place growth only occurs when the existing pointer already satisfies the requested alignment; otherwise the data is copied to a newly aligned block.
`realloc_aligned(NULL, size, 0, alignment)` allocates (like `malloc_aligned`), and `realloc_aligned(ptr, 0, copy_len, alignment)` frees `ptr`.
#### `fio_malloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
malloc_aligned)(size_t size,
size_t alignment);
```
Equivalent to `fio_realloc_aligned(NULL, size, 0, alignment)`. A zero `size` returns the shared zero-allocation pointer (aligned only to `FIO_MEMORY_ALIGN_SIZE`), which may be freed or realloc'd normally.
#### `fio_calloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc_aligned)(size_t size_per_unit,
size_t unit_count,
size_t alignment);
```
Same as `fio_malloc_aligned(size_per_unit * unit_count, alignment)`, but zeroes the (rounded) allocation. Returns `NULL` on size overflow.
#### `fio_alloc_size`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, alloc_size)(size_t minimum_bytes);
```
Returns the number of usable bytes the allocator actually reserves for a request of `minimum_bytes` (the size class): rounded up to `FIO_MEMORY_ALIGN_SIZE` for arena/big-block allocations, and to whole pages (minus the header offset) for page-backed ones. Alignment padding is reserved in addition to the returned value. With `FIO_MEMORY_DISABLE` the system allocator exposes no rounding guarantee and `minimum_bytes` is returned unchanged.
### Alignment abstraction macros
```c
FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment)
FIO_MEM_FREE_ALIGNED(ptr, size)
FIO_MEM_ALLOC_SIZE(size)
```
`FIO_MEM_REALLOC_ALIGNED` mirrors `FIO_MEM_REALLOC` with an added `alignment` argument and **assigns** the result to `ptr` (test with `FIO_ASSERT_ALLOC(ptr)` after the call, restoring a saved copy on failure). `FIO_MEM_FREE_ALIGNED` releases memory obtained through the aligned API, and `FIO_MEM_ALLOC_SIZE` mirrors `fio_alloc_size`.
The macros route to the active allocator, exactly like `FIO_MEM_REALLOC` does: the fio allocator when it was included (`H___FIO_MALLOC___H`), or portable system-allocator fallbacks otherwise. Template-local variants (`FIO_MEM_REALLOC_ALIGNED_`, `FIO_MEM_FREE_ALIGNED_`) exist for `FIO_MEMORY_NAME` template scope, mirroring `FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`.
**Pairing rule:** with the fio allocator, `free` accepts any pointer returned by the aligned API (interior pointers included), so `FIO_MEM_FREE_ALIGNED` is identical to `FIO_MEM_FREE`. Under `FIO_MEMORY_DISABLE` on Windows the aligned API uses `_aligned_malloc` / `_aligned_realloc`, whose memory **must** be released with `FIO_MEM_FREE_ALIGNED` (`_aligned_free`) — plain `free` is heap corruption. On POSIX both map to `free`. Custom allocators that stash alignment bookkeeping near the returned pointer should hook the macro pair, not the fio functions.
### Alignment guarantees per configuration
| Configuration | Default alignment | Aligned API backend | Max alignment |
|---|---|---|---|
| fio allocator (any OS) | `FIO_MEMORY_ALIGN_SIZE` (64) | interior-pointer slice / big-block / page-backed | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, POSIX | `_Alignof(max_align_t)` | `posix_memalign` + `free` | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, Windows | `_Alignof(max_align_t)` (16) | `_aligned_malloc` / `_aligned_realloc` / `_aligned_free` | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, other | `_Alignof(max_align_t)` | over-allocation with stashed raw pointer | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
When `FIO_MALLOC_OVERRIDE_SYSTEM` is defined, the libc aligned-allocation family is overridden as well (`aligned_alloc`, `posix_memalign`, and on Windows `_aligned_malloc` / `_aligned_realloc` / `_aligned_free`), so libc-aligned pointers never reach the fio allocator (and vice versa).
---
## Configuration Macros
#### `FIO_MEMORY_NAME`
```c
#define FIO_MEMORY_NAME fio
```
Required prefix for the allocator's public symbols. Must be defined before including the header.
#### `FIO_MALLOC`
```c
#define FIO_MALLOC
```
Shortcut that defines the allocator with prefix `fio` and tunes it for general use. It also redefines `FIO_MEM_REALLOC`, `FIO_MEM_FREE`, `FIO_MEM_REALLOC_IS_SAFE`, and `FIO_MEM_ALIGNMENT_SIZE`.
#### `FIO_MEMORY_DISABLE`
```c
#define FIO_MEMORY_DISABLE
```
Bypasses the custom allocator and routes all allocations to the system `malloc`/`free`.
#### `FIO_MEMORY_ALIGN_LOG`
```c
#define FIO_MEMORY_ALIGN_LOG 6
```
Log2 of the allocation alignment. Clamped to the range `3`–`10` (8 to 1024 bytes). Default is `6` (64 bytes).
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG 21
```
Log2 of the chunk size allocated from the system. Clamped to `17`–`24`. Default is `21` (~2 MB).
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG 2
```
Log2 of the number of blocks per system chunk. Range `0`–`4`. Affects fragmentation and the threshold for big allocations.
#### `FIO_MEMORY_ENABLE_BIG_ALLOC`
```c
#define FIO_MEMORY_ENABLE_BIG_ALLOC 1
```
When enabled, large allocations can consume an entire chunk as a single big block. May increase fragmentation for long-lived objects.
#### `FIO_MEMORY_ARENA_COUNT`
```c
#define FIO_MEMORY_ARENA_COUNT -1
```
Number of arenas. Negative or zero means dynamic selection based on CPU core count, capped by `FIO_MEMORY_ARENA_COUNT_MAX` and fallback `FIO_MEMORY_ARENA_COUNT_FALLBACK`.
#### `FIO_MEMORY_ARENA_COUNT_FALLBACK`
```c
#define FIO_MEMORY_ARENA_COUNT_FALLBACK 24
```
Default arena count when dynamic CPU detection fails.
#### `FIO_MEMORY_ARENA_COUNT_MAX`
```c
#define FIO_MEMORY_ARENA_COUNT_MAX 64
```
Upper bound for dynamic arena count.
#### `FIO_MEMORY_USE_THREAD_MUTEX`
```c
#define FIO_MEMORY_USE_THREAD_MUTEX 0
```
When `1`, arenas protect themselves with `pthread` mutexes instead of spinlocks. Default is `1` when `FIO_MEMORY_ARENA_COUNT` is positive.
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS \
FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT
```
When `1`, allocations return zeroed memory and `realloc2` zeroes uncopied bytes. Default is `1`.
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT 1
```
Default value for `FIO_MEMORY_INITIALIZE_ALLOCATIONS`.
#### `FIO_MEMORY_CACHE_SLOTS`
```c
#define FIO_MEMORY_CACHE_SLOTS 4
```
Number of freed chunks to keep cached before returning memory to the system.
#### `FIO_MEMORY_WARMUP`
```c
#define FIO_MEMORY_WARMUP 0
```
If set to a positive number, that many arenas pre-allocate a block at startup. Usually best left at `0`.
#### `FIO_MEM_SYS_ALLOC`, `FIO_MEM_SYS_REALLOC`, `FIO_MEM_SYS_FREE`
```c
#define FIO_MEM_SYS_ALLOC(pages, alignment_log) /* ... */
#define FIO_MEM_SYS_REALLOC(ptr, old_pages, new_pages, alignment_log) /* ... */
#define FIO_MEM_SYS_FREE(ptr, pages) /* ... */
```
Override all three to replace the system allocation backend. Alignment is essential and may be large.
---
## Introspection
These functions are exposed for debugging. Treat them as unstable.
#### `fio_malloc_arenas`
```c
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arenas)(void);
```
Returns the number of arenas.
#### `fio_malloc_block_size`
```c
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_block_size)(void);
```
Returns the block size used for arena allocations.
#### `fio_malloc_sys_alloc_size`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_sys_alloc_size)(void);
```
Returns the system chunk size in bytes.
#### `fio_malloc_cache_slots`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_cache_slots)(void);
```
Returns the configured number of cache slots.
#### `fio_malloc_alignment`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment)(void);
```
Returns the allocation alignment in bytes.
#### `fio_malloc_alignment_log`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment_log)(void);
```
Returns log2 of the allocation alignment.
#### `fio_malloc_alloc_limit`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alloc_limit)(void);
```
Returns the size above which allocations bypass arena blocks.
#### `fio_malloc_arena_alloc_limit`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arena_alloc_limit)(void);
```
Returns the arena block size limit.
#### `fio_malloc_print_state`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_state)(void);
```
Prints allocator state to `stderr`.
#### `fio_malloc_print_free_block_list`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_free_block_list)(void);
```
Prints the free-block list to `stderr`.
#### `fio_malloc_print_settings`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_settings)(void);
```
Prints allocator settings to `stderr`.
---
## Example
```c
#define FIO_MALLOC
#include "fio-stl.h"
int main(void) {
char *buf = fio_malloc(256);
if (!buf) return -1;
fio_memcpy(buf, "hello", 5);
fprintf(stderr, "%s\n", buf);
char *buf2 = fio_realloc2(buf, 512, 5);
if (!buf2) {
fio_free(buf);
return -1;
}
buf = buf2;
fio_free(buf);
return 0;
}
```
------------------------------------------------------------
# String Core
```c
#define FIO_STR
#include "fio-stl.h"
```
Binary-safe string helpers for building, editing, escaping, encoding, and comparing text. This module works with the lightweight `fio_str_info_s` and `fio_buf_info_s` descriptors, plus two owned string types: copy-on-write `fio_bstr` and hash-map key string `fio_keystr_s`.
All authoring helpers take a `fio_string_realloc_fn` callback so they can grow either a stack buffer or a heap allocation.
---
## Types
#### `fio_string_realloc_fn`
```c
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len);
```
Callback that grows `dest->buf` to at least `len + 1` bytes and updates `dest->capa`. Returns `0` on success, `-1` on failure.
#### `fio_string_write_s`
```c
typedef struct {
size_t klass;
union {
struct { size_t len; const char *buf; } str;
double f;
int64_t i;
uint64_t u;
} info;
} fio_string_write_s;
```
Argument item used by `fio_string_write2` and `fio_bstr_write2`. The `klass` field selects the union member: `1` = string, `2` = signed int, `3` = unsigned int, `4` = hex, `5` = binary, `6` = double.
---
## Authorship Helpers
#### `fio_string_write`
```c
SFUNC int fio_string_write(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *restrict src,
size_t len);
```
Appends `len` bytes from `src` to `dest`. Grows `dest` if `reallocate` is provided; otherwise truncates when full. Always NUL-terminates on success.
**Parameters:**
- `dest` — target string descriptor. Must have a valid buffer and `capa >= len`.
- `reallocate` — growth callback, may be `NULL`.
- `src` — data to append.
- `len` — bytes to append.
**Returns:** `0` on success, `-1` if reallocation was needed and failed.
#### `fio_string_replace`
```c
SFUNC int fio_string_replace(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
intptr_t start_pos,
size_t overwrite_len,
const void *src,
size_t len);
```
Inserts, overwrites, or deletes a slice. Negative `start_pos` counts from the end (`-1` = last byte). When `overwrite_len` is `0`, the data is inserted. When `len` is `0`, the slice is deleted.
**Parameters:**
- `start_pos` — byte offset where editing begins.
- `overwrite_len` — bytes to overwrite or remove.
- `src` — replacement data, ignored if `len == 0`.
- `len` — replacement length.
**Returns:** `0` on success, `-1` on failure.
#### `fio_string_write2`
```c
SFUNC int fio_string_write2(fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate,
const fio_string_write_s srcs[]);
#define fio_string_write2(dest, reallocate, ...) \
fio_string_write2((dest), \
(reallocate), \
(fio_string_write_s[]){__VA_ARGS__, {0}})
```
Appends a mixed list of strings and numbers in a single pass. The macro builds a temporary `fio_string_write_s[]` array and appends a zero terminator.
```c
fio_string_write2(&str, my_realloc,
FIO_STRING_WRITE_STR1("The answer is: "),
FIO_STRING_WRITE_NUM(42),
FIO_STRING_WRITE_STR2(" (0x", 4),
FIO_STRING_WRITE_HEX(42),
FIO_STRING_WRITE_STR2(")", 1));
```
#### `FIO_STRING_WRITE_*`
```c
#define FIO_STRING_WRITE_STR1(str_) /* string with FIO_STRLEN length */
#define FIO_STRING_WRITE_STR2(str_, len_) /* string with explicit length */
#define FIO_STRING_WRITE_STR_INFO(str_) /* from fio_str_info_s */
#define FIO_STRING_WRITE_NUM(num) /* signed integer */
#define FIO_STRING_WRITE_UNUM(num) /* unsigned integer */
#define FIO_STRING_WRITE_HEX(num) /* hex representation */
#define FIO_STRING_WRITE_BIN(num) /* binary representation */
#define FIO_STRING_WRITE_FLOAT(num) /* double */
```
Helper macros that build `fio_string_write_s` items for `fio_string_write2`.
---
## Numeral Helpers
#### `fio_string_write_i`
```c
SFUNC int fio_string_write_i(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int64_t i);
```
Appends a signed base-10 integer.
#### `fio_string_write_u`
```c
SFUNC int fio_string_write_u(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends an unsigned base-10 integer.
#### `fio_string_write_hex`
```c
SFUNC int fio_string_write_hex(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends a lowercase hex representation.
#### `fio_string_write_bin`
```c
SFUNC int fio_string_write_bin(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends a binary representation.
---
## printf Support
#### `fio_string_printf`
```c
SFUNC FIO___PRINTF_STYLE(3, 0) int fio_string_printf(
fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *format,
...);
```
Appends formatted output, growing the buffer as needed.
#### `fio_string_vprintf`
```c
SFUNC FIO___PRINTF_STYLE(3, 0) int fio_string_vprintf(
fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *format,
va_list argv);
```
`va_list` variant of `fio_string_printf`.
---
## Escaping and Encoding
#### `fio_string_write_escape`
```c
SFUNC int fio_string_write_escape(fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
```
Escapes `raw` using JSON string rules and appends the result.
#### `fio_string_write_unescape`
```c
SFUNC int fio_string_write_unescape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *enscaped,
size_t enscaped_len);
```
Unescapes JSON-style data and appends it.
#### `fio_string_write_base32enc` / `fio_string_write_base32dec`
```c
SFUNC int fio_string_write_base32enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_base32dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
Base32 encode / decode.
#### `fio_string_write_base64enc` / `fio_string_write_base64dec`
```c
SFUNC int fio_string_write_base64enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len,
uint8_t url_encoded);
SFUNC int fio_string_write_base64dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
Base64 encode / decode. Pass `url_encoded = 1` to use URL-safe alphabet.
#### `fio_string_write_url_enc` / `fio_string_write_url_dec` / `fio_string_write_path_dec`
```c
SFUNC int fio_string_write_url_enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_url_dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
SFUNC int fio_string_write_path_dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
URL (percent) encoding and decoding. `url_dec` converts `+` to space; `path_dec` does not.
#### `fio_string_write_html_escape` / `fio_string_write_html_unescape`
```c
SFUNC int fio_string_write_html_escape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_html_unescape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *enscaped,
size_t enscaped_len);
```
HTML escape / unescape. The unescape implementation is minimal and incomplete.
---
## File Reading
#### `fio_string_readfd`
```c
SFUNC int fio_string_readfd(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int fd,
intptr_t start_at,
size_t limit);
```
Reads up to `limit` bytes from seekable file descriptor `fd` starting at `start_at`. Negative `start_at` counts from EOF. `limit == 0` reads to EOF.
#### `fio_string_readfile`
```c
SFUNC int fio_string_readfile(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *filename,
intptr_t start_at,
size_t limit);
```
Opens `filename` and reads its contents (or a slice) into `dest`.
#### `fio_string_getdelim_fd`
```c
SFUNC int fio_string_getdelim_fd(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int fd,
intptr_t start_at,
char delim,
size_t limit);
```
Reads from `fd` until `delim` or EOF.
#### `fio_string_getdelim_file`
```c
SFUNC int fio_string_getdelim_file(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *filename,
intptr_t start_at,
char delim,
size_t limit);
```
Same as `fio_string_getdelim_fd` but opens and closes the file.
---
## Memory Helpers
#### `fio_string_capa4len`
```c
FIO_IFUNC size_t fio_string_capa4len(size_t new_len);
```
Returns a 16-byte-aligned capacity for `new_len`.
#### Default Allocator Callbacks
```c
#define FIO_STRING_SYS_REALLOC fio_string_sys_reallocate
#define FIO_STRING_REALLOC fio_string_default_reallocate
#define FIO_STRING_ALLOC_COPY fio_string_default_allocate_copy
#define FIO_STRING_ALLOC_KEY fio_string_default_key_alloc
#define FIO_STRING_FREE fio_string_default_free
#define FIO_STRING_FREE2 fio_string_default_free2
#define FIO_STRING_FREE_KEY fio_string_default_free_key
#define FIO_STRING_FREE_NOOP fio_string_default_free_noop
#define FIO_STRING_FREE_NOOP2 fio_string_default_free_noop2
```
Convenience macros pointing to the default callbacks.
#### `fio_string_default_reallocate`
```c
SFUNC int fio_string_default_reallocate(fio_str_info_s *dst, size_t len);
```
Grows `dst` using the current facil.io allocator (`FIO_MEM_REALLOC_`).
#### `fio_string_default_allocate_copy`
```c
SFUNC int fio_string_default_allocate_copy(fio_str_info_s *dest, size_t new_capa);
```
Allocates a new buffer and copies existing data into it.
#### `fio_string_default_free`
```c
SFUNC void fio_string_default_free(void *);
```
Frees a buffer allocated with the default callbacks.
---
## UTF-8 Helpers
#### `fio_string_utf8_valid`
```c
SFUNC bool fio_string_utf8_valid(fio_str_info_s str);
```
Returns `true` if `str` is valid UTF-8.
#### `fio_string_utf8_len`
```c
SFUNC size_t fio_string_utf8_len(fio_str_info_s str);
```
Returns the number of Unicode code points, or `0` if invalid.
#### `fio_string_utf8_valid_code_point`
```c
SFUNC size_t fio_string_utf8_valid_code_point(const void *u8c, size_t buf_len);
```
Returns the byte length of the UTF-8 code point at `u8c` (`1`–`4`), or `0` if invalid.
#### `fio_string_utf8_select`
```c
SFUNC int fio_string_utf8_select(fio_str_info_s str,
intptr_t *pos,
size_t *len);
```
Converts a UTF-8 character range (`pos`, `len`) into raw byte offsets. Returns `-1` on error and sets `pos` to `-1` if the string is invalid before the selection.
---
## Comparison Helpers
#### `fio_string_is_greater_buf`
```c
SFUNC int fio_string_is_greater_buf(fio_buf_info_s a, fio_buf_info_s b);
```
Returns `1` if `a` is lexicographically greater than `b`; `0` otherwise (including equality).
#### `fio_string_is_greater`
```c
FIO_IFUNC int fio_string_is_greater(fio_str_info_s a, fio_str_info_s b);
```
Same comparison for `fio_str_info_s`.
---
## Binary String (`fio_bstr`)
A reference-counted, copy-on-write, NUL-terminated binary string. The public pointer points at the first character; metadata lives just before it.
#### `fio_bstr_reserve`
```c
FIO_IFUNC char *fio_bstr_reserve(char *bstr, size_t len);
```
Ensures at least `len` additional bytes are available.
#### `fio_bstr_copy`
```c
FIO_IFUNC char *fio_bstr_copy(char *bstr);
```
Returns a shallow copy, incrementing the reference count. Falls back to a real copy if the reference count overflows.
#### `fio_bstr_free`
```c
FIO_IFUNC void fio_bstr_free(char *bstr);
```
Decrements the reference count and frees memory when it reaches zero.
#### `fio_bstr_info` / `fio_bstr_buf` / `fio_bstr_len` / `fio_bstr_len_set`
```c
FIO_IFUNC fio_str_info_s fio_bstr_info(const char *bstr);
FIO_IFUNC fio_buf_info_s fio_bstr_buf(const char *bstr);
FIO_IFUNC size_t fio_bstr_len(const char *bstr);
FIO_IFUNC char *fio_bstr_len_set(char *bstr, size_t len);
```
Inspect or resize the string. `fio_bstr_len_set` may reallocate if the string is shared or too small.
#### `fio_bstr_write` and Friends
```c
FIO_IFUNC char *fio_bstr_write(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_replace(char *bstr, intptr_t start_pos,
size_t overwrite_len, const void *src,
size_t len);
FIO_IFUNC char *fio_bstr_write2(char *bstr, const fio_string_write_s srcs[]);
#define fio_bstr_write2(bstr, ...) \
fio_bstr_write2(bstr, (fio_string_write_s[]){__VA_ARGS__, {0}})
FIO_IFUNC char *fio_bstr_write_i(char *bstr, int64_t num);
FIO_IFUNC char *fio_bstr_write_u(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_hex(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_bin(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_escape(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_unescape(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_base64enc(char *bstr, const void *src,
size_t len, uint8_t url_encoded);
FIO_IFUNC char *fio_bstr_write_base64dec(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_url_enc(char *bstr, const void *data, size_t len);
FIO_IFUNC char *fio_bstr_write_url_dec(char *bstr, const void *encoded, size_t len);
FIO_IFUNC char *fio_bstr_write_html_escape(char *bstr, const void *raw, size_t len);
FIO_IFUNC char *fio_bstr_write_html_unescape(char *bstr, const void *escaped,
size_t len);
FIO_IFUNC char *fio_bstr_readfd(char *bstr, int fd, intptr_t start_at,
intptr_t limit);
FIO_IFUNC char *fio_bstr_readfile(char *bstr, const char *filename,
intptr_t start_at, intptr_t limit);
FIO_IFUNC char *fio_bstr_getdelim_file(char *bstr, const char *filename,
intptr_t start_at, char delim,
size_t limit);
FIO_IFUNC char *fio_bstr_getdelim_fd(char *bstr, int fd, intptr_t start_at,
char delim, size_t limit);
FIO_IFUNC char *fio_bstr_printf(char *bstr, const char *format, ...);
```
Every `fio_bstr_*` function returns the possibly-reallocated string pointer. Always assign the result back.
#### `fio_bstr_is_greater` / `fio_bstr_is_eq` / `fio_bstr_is_eq2info` / `fio_bstr_is_eq2buf`
```c
FIO_SFUNC int fio_bstr_is_greater(const char *a, const char *b);
FIO_SFUNC int fio_bstr_is_eq(const char *a_, const char *b_);
FIO_SFUNC int fio_bstr_is_eq2info(const char *a_, fio_str_info_s b);
FIO_SFUNC int fio_bstr_is_eq2buf(const char *a_, fio_buf_info_s b);
```
Comparison helpers.
#### `fio_bstr_reallocate`
```c
SFUNC int fio_bstr_reallocate(fio_str_info_s *dest, size_t len);
```
Default reallocate callback for `fio_bstr`.
---
## Key String (`fio_keystr_s`)
A compact key type used by hash maps. Small strings are embedded in the struct; larger ones store a pointer and length. Not NUL-terminated.
#### `fio_keystr_tmp`
```c
FIO_IFUNC fio_keystr_s fio_keystr_tmp(const char *buf, uint32_t len);
```
Returns a temporary key referencing `buf`/`len`.
#### `fio_keystr_init`
```c
FIO_SFUNC fio_keystr_s fio_keystr_init(fio_str_info_s str,
void *(*alloc_func)(size_t len));
```
Copies `str` into a persistent key. Strings short enough are embedded; otherwise `alloc_func` is used. Pass `capa == FIO_KEYSTR_CONST` to store a borrowed pointer.
#### `fio_keystr_destroy`
```c
FIO_SFUNC void fio_keystr_destroy(fio_keystr_s *key,
void (*free_func)(void *, size_t));
```
Releases a key allocated by `fio_keystr_init`.
#### `fio_keystr_buf` / `fio_keystr_info` / `fio_keystr_is_eq` / `fio_keystr_is_eq2` / `fio_keystr_is_eq3` / `fio_keystr_hash`
```c
FIO_IFUNC fio_buf_info_s fio_keystr_buf(fio_keystr_s *str);
FIO_IFUNC fio_str_info_s fio_keystr_info(fio_keystr_s *str);
FIO_IFUNC int fio_keystr_is_eq(fio_keystr_s a, fio_keystr_s b);
FIO_IFUNC int fio_keystr_is_eq2(fio_keystr_s a_, fio_str_info_s b);
FIO_IFUNC int fio_keystr_is_eq3(fio_keystr_s a_, fio_buf_info_s b);
FIO_IFUNC uint64_t fio_keystr_hash(fio_keystr_s a);
```
Inspect, compare, and hash key strings.
#### `FIO_KEYSTR_CONST`
```c
#define FIO_KEYSTR_CONST ((size_t)-1LL)
```
Magic `capa` value for `fio_keystr_init` that keeps a borrowed pointer instead of allocating.
---
## Example
```c
#define FIO_STR
#include "fio-stl.h"
int my_realloc(fio_str_info_s *dest, size_t len) {
size_t capa = fio_string_capa4len(len);
char *tmp = realloc(dest->buf, capa);
if (!tmp) return -1;
dest->buf = tmp;
dest->capa = capa;
return 0;
}
int main(void) {
char buf[32];
fio_str_info_s str = FIO_STR_INFO3(buf, 0, 32);
fio_string_write(&str, my_realloc, "answer: ", 8);
fio_string_write_i(&str, my_realloc, 42);
fprintf(stderr, "%s\n", str.buf);
char *b = NULL;
b = fio_bstr_write(b, "hello ", 6);
b = fio_bstr_write(b, "world", 5);
fprintf(stderr, "%s (len=%zu)\n", b, fio_bstr_len(b));
fio_bstr_free(b);
return 0;
}
```
------------------------------------------------------------
# GFM Markdown Parser
```c
#define FIO_GFM
#include "fio-stl.h"
```
A flat-state, non-recursive parser for GitHub Flavored Markdown. It emits block and inline events through three callbacks — push, write, pop — without allocating heap memory itself. `fio_gfm_parse` is the only public entry point.
The parser depends on `FIO_ENTITY` for HTML entity decoding.
---
## Configuration Macros
#### `FIO_GFM_MAX_DEPTH`
```c
#define FIO_GFM_MAX_DEPTH 255
```
Maximum container nesting depth. Must fit in a byte (≤ 255).
#### `FIO_GFM_MAX_TABLE_COLUMNS`
```c
#define FIO_GFM_MAX_TABLE_COLUMNS 64
```
Maximum table columns. Alignment is bit-packed, so four columns fit in one byte.
#### `FIO_GFM_REF_CACHE_SIZE`
```c
#define FIO_GFM_REF_CACHE_SIZE 128
```
Maximum reference definitions held in the fast inline cache.
---
## Error Codes
#### `FIO_GFM_ERR_GENERIC`, `FIO_GFM_ERR_DEPTH`, `FIO_GFM_ERR_INPUT`
```c
#define FIO_GFM_ERR_GENERIC -1
#define FIO_GFM_ERR_DEPTH -2
#define FIO_GFM_ERR_INPUT -3
```
Negative error codes used internally and returned through `fio_gfm_parse`.
---
## Flags
#### `FIO_GFM_F_TIGHT`
```c
#define FIO_GFM_F_TIGHT ((uint8_t)1U << 0)
```
The list is tight (no blank lines between items).
#### `FIO_GFM_F_LOOSE_SEEN`
```c
#define FIO_GFM_F_LOOSE_SEEN ((uint8_t)1U << 1)
```
A blank line was seen between list items.
#### `FIO_GFM_F_TASK`
```c
#define FIO_GFM_F_TASK ((uint8_t)1U << 2)
```
The list item is a task-list item.
#### `FIO_GFM_F_TASK_CHECKED`
```c
#define FIO_GFM_F_TASK_CHECKED ((uint8_t)1U << 3)
```
The task-list marker is checked (`[x]`).
---
## Types
#### `fio_gfm_type_e`
```c
typedef enum {
FIO_GFM_PARAGRAPH = 1,
FIO_GFM_HEADING,
FIO_GFM_THEMATIC_BREAK,
FIO_GFM_BLOCKQUOTE,
FIO_GFM_LIST_UNORDERED,
FIO_GFM_LIST_ORDERED,
FIO_GFM_LIST_ITEM,
FIO_GFM_CODE_BLOCK,
FIO_GFM_HTML_BLOCK,
FIO_GFM_TABLE,
FIO_GFM_TABLE_ROW,
FIO_GFM_TABLE_CELL,
FIO_GFM_EMPHASIS,
FIO_GFM_STRONG,
FIO_GFM_STRIKETHROUGH,
FIO_GFM_LINK,
FIO_GFM_TEXT,
FIO_GFM_SOFT_BREAK,
FIO_GFM_HARD_BREAK,
FIO_GFM_CODE_SPAN,
FIO_GFM_IMAGE,
FIO_GFM_AUTOLINK,
FIO_GFM_INLINE_HTML,
FIO_GFM_FOOTNOTE_REF,
} fio_gfm_type_e;
```
Event type. Values `1`–`12` are block sections; `13`–`16` are inline sections; `17`–`23` are text and leaf content emitted with `write`.
#### `fio_gfm_align_e`
```c
typedef enum {
FIO_GFM_ALIGN_NONE = 0,
FIO_GFM_ALIGN_LEFT,
FIO_GFM_ALIGN_RIGHT,
FIO_GFM_ALIGN_CENTER,
} fio_gfm_align_e;
```
Table cell alignment.
#### `fio_gfm_event_s`
```c
typedef struct {
void *udata;
fio_buf_info_s source;
fio_buf_info_s text;
fio_buf_info_s marker;
fio_buf_info_s info;
fio_buf_info_s destination;
fio_buf_info_s title;
fio_buf_info_s reference;
uint32_t list_start;
uint16_t columns;
uint16_t column;
uint8_t type;
uint8_t heading_level;
uint8_t flags;
uint8_t align;
uint8_t padding;
} fio_gfm_event_s;
```
Event passed to every callback.
**Fields:**
- `udata` — live user pointer; the parser copies it back after each callback.
- `source` — full source slice for this event.
- `text` — literal text / label / code content.
- `marker` — marker slice (`#`, fence, list bullet, etc.).
- `info` — fenced code info string (language tag).
- `destination` — link/image/autolink URL.
- `title` — link/image title.
- `reference` — reference label for ref-style links/images.
- `list_start` — ordered list start number.
- `columns` / `column` — table column count and cell index.
- `type` — `fio_gfm_type_e` value.
- `heading_level` — `1`–`6` for headings.
- `flags` — `FIO_GFM_F_*`.
- `align` — `fio_gfm_align_e` for table cells.
- `padding` — virtual leading spaces from tab expansion.
#### `fio_gfm_callbacks_s`
```c
typedef struct {
int (*push)(fio_gfm_event_s *e);
int (*write)(fio_gfm_event_s *e);
int (*pop)(fio_gfm_event_s *e);
} fio_gfm_callbacks_s;
```
Parser callbacks. All are optional. Return `0` to continue, non-zero to abort. Positive values are user errors; negative values are reserved for parser errors.
---
## Parsing
#### `fio_gfm_parse`
```c
SFUNC size_t fio_gfm_parse(const fio_gfm_callbacks_s *callbacks,
void *udata,
fio_buf_info_s source);
```
Parses a complete Markdown document and emits events. The document must be fully available in `source`; streaming is not supported.
**Parameters:**
- `callbacks` — pointer to callback struct.
- `udata` — user pointer copied into every event; callbacks may update it.
- `source` — full document bytes.
**Returns:** the number of bytes consumed. If this equals `source.len`, parsing completed. A smaller value means a callback aborted or a parser error occurred.
---
## Example
```c
#define FIO_GFM
#include "fio-stl.h"
static int on_push(fio_gfm_event_s *e) {
fprintf(stderr, "open type=%u\n", e->type);
return 0;
}
static int on_write(fio_gfm_event_s *e) {
fprintf(stderr, "text: %.*s\n", (int)e->text.len, e->text.buf);
return 0;
}
static int on_pop(fio_gfm_event_s *e) {
fprintf(stderr, "close type=%u\n", e->type);
return 0;
}
int main(void) {
fio_gfm_callbacks_s cb = {
.push = on_push,
.write = on_write,
.pop = on_pop,
};
fio_buf_info_s src = FIO_BUF_INFO2("# Hello\n\nworld.\n", 16);
size_t n = fio_gfm_parse(&cb, NULL, src);
fprintf(stderr, "consumed %zu of %zu bytes\n", n, src.len);
return 0;
}
```
------------------------------------------------------------
# POSIX Portable Polling API
```c
#define FIO_POLL
#include "fio-stl.h"
```
A small abstraction over `poll`, `epoll`, and `kqueue`. Define `FIO_POLL_ENGINE_POLL`, `FIO_POLL_ENGINE_EPOLL`, or `FIO_POLL_ENGINE_KQUEUE` before inclusion to force a backend; otherwise the best available engine is selected automatically.
The API is the same for every backend. Backend-specific details live in:
- [`102 poll poll.md`](https://facil.io/0.8.x/posix-portable-polling-api/)
- [`102 poll epoll.md`](https://facil.io/0.8.x/posix-polling-epoll-backend/)
- [`102 poll kqueue.md`](https://facil.io/0.8.x/posix-polling-kqueue-backend/)
---
## Configuration Macros
#### `FIO_POLL_POSSIBLE_FLAGS`
```c
#define FIO_POLL_POSSIBLE_FLAGS (POLLIN | POLLOUT | POLLPRI)
```
Mask of event flags the polling layer recognizes.
#### `FIO_POLL_MAX_EVENTS`
```c
#define FIO_POLL_MAX_EVENTS 128
```
Maximum events returned in one `fio_poll_review` call. Relevant for epoll and kqueue. Larger on 32-bit platforms.
---
## Types
#### `fio_poll_s`
```c
typedef struct fio_poll_s fio_poll_s;
```
Opaque polling object. The actual layout depends on the selected backend.
#### `fio_poll_settings_s`
```c
typedef struct {
void (*on_data)(void *udata);
void (*on_ready)(void *udata);
void (*on_close)(void *udata);
} fio_poll_settings_s;
```
Callback settings. Each callback receives the `udata` pointer supplied when the file descriptor was monitored.
- `on_data` — data is available to read.
- `on_ready` — the socket is ready to write.
- `on_close` — connection closed or an error occurred.
---
## Functions
#### `fio_poll_engine`
```c
FIO_IFUNC const char *fio_poll_engine(void);
```
Returns the name of the active backend (`"epoll"`, `"kqueue"`, or `"poll"`) as a constant string.
#### `fio_poll_init`
```c
FIO_IFUNC void fio_poll_init(fio_poll_s *p, fio_poll_settings_s settings);
#define fio_poll_init(p, ...) \
fio_poll_init((p), (fio_poll_settings_s){__VA_ARGS__})
```
Initializes the polling object. The macro form accepts designated initializers.
**Parameters:**
- `p` — pointer to the polling object.
- `settings` — callback configuration.
```c
fio_poll_init(&poll,
.on_data = on_data,
.on_ready = on_ready,
.on_close = on_close);
```
#### `fio_poll_destroy`
```c
FIO_IFUNC void fio_poll_destroy(fio_poll_s *p);
```
Frees backend resources held by the polling object.
#### `fio_poll_monitor`
```c
SFUNC int fio_poll_monitor(fio_poll_s *p,
fio_socket_i fd,
void *udata,
unsigned short flags);
```
Adds `fd` to the poll set, updates its `udata`, or adds events. Recognized flags are `POLLIN`, `POLLOUT`, and `POLLPRI`; other flags may be ignored. Monitoring is one-shot: once an event fires, it is removed until re-armed.
**Returns:** `0` on success, `-1` on error.
#### `fio_poll_review`
```c
SFUNC int fio_poll_review(fio_poll_s *p, size_t timeout);
```
Waits up to `timeout` milliseconds for events and dispatches callbacks. Adding a file descriptor from another thread while `fio_poll_review` is running will not include that descriptor until the next call.
**Returns:** the number of events dispatched, or `-1` on error.
#### `fio_poll_forget`
```c
SFUNC int fio_poll_forget(fio_poll_s *p, fio_socket_i fd);
```
Removes `fd` from the poll set.
**Returns:** `0` on success, `-1` on error.
---
## Example
```c
#define FIO_POLL
#include "fio-stl.h"
static void on_data(void *udata) { fprintf(stderr, "readable\n"); }
static void on_ready(void *udata) { fprintf(stderr, "writable\n"); }
static void on_close(void *udata) { fprintf(stderr, "closed\n"); }
int main(void) {
fio_poll_s p;
fio_poll_init(&p,
.on_data = on_data,
.on_ready = on_ready,
.on_close = on_close);
fio_poll_monitor(&p, some_fd, NULL, POLLIN);
for (int i = 0; i < 10; ++i)
fio_poll_review(&p, 1000);
fio_poll_destroy(&p);
return 0;
}
```
------------------------------------------------------------
# POSIX Polling — epoll Backend
```c
#define FIO_POLL_ENGINE_EPOLL
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of Linux `epoll`. Normally selected automatically on Linux; define `FIO_POLL_ENGINE_EPOLL` to force it.
The public API is documented in [`102 poll api.md`](https://facil.io/0.8.x/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
struct pollfd fds[2];
int fd[2];
};
```
Two internal `epoll` file descriptors are used: one for `POLLOUT` readiness and one for `POLLIN`/error events. A `pollfd` pair wakes the internal `poll` used to wait on both epoll FDs at once.
#### `fio_poll_engine`
Returns `"epoll"`.
#### Fork Safety
A state callback re-creates the epoll FDs in the child process after `fork`.
---
## Notes
- Events are monitored in one-shot mode (`EPOLLONESHOT`). After an event fires, the descriptor must be re-armed with `fio_poll_monitor`.
- Error and hang-up events are dispatched through the read-side epoll FD and delivered to `on_close`.
- `EPOLLRDHUP` and `EPOLLHUP` are always included in the monitored events.
------------------------------------------------------------
# POSIX Polling — kqueue Backend
```c
#define FIO_POLL_ENGINE_KQUEUE
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of BSD/macOS `kqueue`. Normally selected automatically when `` is available; define `FIO_POLL_ENGINE_KQUEUE` to force it.
The public API is documented in [`102 poll api.md`](https://facil.io/0.8.x/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
int fd;
};
```
A single kqueue descriptor is used for both read and write filters.
#### `fio_poll_engine`
Returns `"kqueue"`.
#### Fork Safety
A state callback re-creates the kqueue descriptor in the child process after `fork`.
---
## Notes
- Read and write filters are registered with `EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT`.
- `EV_EOF` and `EV_ERROR` flags are routed to `on_close`.
- `fio_poll_review` uses a `timespec` built from the millisecond timeout.
------------------------------------------------------------
# POSIX Polling — poll Backend
```c
#define FIO_POLL_ENGINE_POLL
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of POSIX `poll()` / Windows `WSAPoll`. This is the fallback backend and is selected automatically when neither epoll nor kqueue is available.
The public API is documented in [`102 poll api.md`](https://facil.io/0.8.x/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
fio___poll_map_s map;
FIO___LOCK_TYPE lock;
};
```
The poll backend keeps monitored descriptors in an internal imap (`fio___poll_map_s`) and snapshots its armed flags before each `poll()` call. Descriptors added or re-armed during `fio_poll_review` update the retained map directly; surviving one-shot flags are restored when the call returns.
`fio_poll_forget` removes the descriptor from the retained map, which also prevents any surviving snapshot flags from being restored. It is not a cancellation guarantee: an event already returned by an in-flight `poll()` call may still be dispatched. This matches the epoll/kqueue backends, whose in-hand event lists always dispatch. As on every backend, `udata` lifetime during dispatch is the caller's responsibility (the facil.io IO layer only forgets from the reactor thread, never concurrently with a review).
One-shot flag accounting strips whole event groups: `WSAPoll` reports sub-band bits (e.g. `POLLRDNORM`) while `POLLIN`/`POLLOUT` are supersets, so a raw bitwise strip would leave band bits (e.g. `POLLRDBAND`) armed on Windows. Fired `POLLIN`/`POLLOUT` groups are therefore stripped in full.
#### `fio_poll_engine`
Returns `"poll"`.
#### `fio_poll_close_all`
```c
SFUNC void fio_poll_close_all(fio_poll_s *p);
```
Additional helper available only in the poll backend. Closes every monitored socket and calls `on_close` for each one. Useful during shutdown.
---
## Notes
- `POLLRDHUP` is used when available; otherwise the backend relies on `POLLHUP`, `POLLERR`, and `POLLNVAL` for close/error detection.
- On Windows, `POLLPRI` is omitted because `WSAPoll` rejects it.
- Fired events are stripped from the descriptor’s flags (one-shot semantics). Surviving flags are restored to retained entries after `poll()` returns; concurrent removals (which delete the retained entry) prevent that restoration.
------------------------------------------------------------
# Task and Timer Queues
```c
#define FIO_QUEUE
#include "fio-stl.h"
```
A thread-safe task queue built from linked ring buffers, plus a timer queue that moves due events into a task queue. Worker threads can be attached to drain the queue in the background.
---
## Configuration
#### `FIO_QUEUE_TASKS_PER_ALLOC`
```c
#define FIO_QUEUE_TASKS_PER_ALLOC 168 /* 338 on 32-bit */
```
Tasks per ring-buffer chunk. Default is chosen so `fio_queue_s` fits in one page. Must not exceed 65535.
---
## Task Queue Types
#### `fio_queue_task_s`
```c
typedef struct {
void (*fn)(void *, void *);
void *udata1;
void *udata2;
} fio_queue_task_s;
```
A single task. `fn` receives both `udata` pointers. Tasks with `fn == NULL` are ignored.
#### `fio_queue_s`
```c
typedef struct {
fio___task_ring_s *r;
fio___task_ring_s *w;
uint32_t count;
FIO___LOCK_TYPE lock;
FIO_LIST_NODE consumers;
fio___task_ring_s mem;
} fio_queue_s;
```
Queue object. Treat as opaque. Allocate on the stack or with `fio_queue_new`.
---
## Task Queue API
#### `FIO_QUEUE_STATIC_INIT`
```c
#define FIO_QUEUE_STATIC_INIT(queue) /* ... */
```
Static initializer for a global queue. Prefer `fio_queue_init` at runtime when possible — it initializes only the bytes that matter.
#### `fio_queue_init`
```c
FIO_IFUNC void fio_queue_init(fio_queue_s *q);
```
Initializes `q` in place.
#### `fio_queue_destroy`
```c
SFUNC void fio_queue_destroy(fio_queue_s *q);
```
Frees ring buffers, stops and joins workers, and re-initializes `q`. Pending tasks are performed only if the queue has workers (see `fio_queue_workers_join`); otherwise they are discarded without being performed. After destruction the queue may be reused, but on some platforms the lock may need explicit re-initialization.
#### `fio_queue_new`
```c
SFUNC fio_queue_s *fio_queue_new(void);
```
Allocates and initializes a queue on the heap.
#### `fio_queue_free`
```c
SFUNC void fio_queue_free(fio_queue_s *q);
```
Destroys `q` and frees the queue object itself.
#### `fio_queue_push`
```c
SFUNC int fio_queue_push(fio_queue_s *q, fio_queue_task_s task);
#define fio_queue_push(q, ...) \
fio_queue_push((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the tail of the queue. The macro accepts named arguments.
**Returns:** `0` on success, `-1` on memory error.
```c
fio_queue_push(&q, .fn = my_task, .udata1 = arg);
```
#### `fio_queue_push_urgent`
```c
SFUNC int fio_queue_push_urgent(fio_queue_s *q, fio_queue_task_s task);
#define fio_queue_push_urgent(q, ...) \
fio_queue_push_urgent((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the head of the queue (LIFO).
**Returns:** `0` on success, `-1` on memory error.
#### `fio_queue_pop`
```c
SFUNC fio_queue_task_s fio_queue_pop(fio_queue_s *q);
```
Removes and returns the next task (FIFO). The returned task has `fn == NULL` if the queue was empty.
#### `fio_queue_perform`
```c
SFUNC int fio_queue_perform(fio_queue_s *q);
```
Pops and performs one task. Returns `-1` if the queue was empty.
#### `fio_queue_perform_all`
```c
SFUNC void fio_queue_perform_all(fio_queue_s *q);
```
Performs every task currently in the queue.
#### `fio_queue_count`
```c
FIO_IFUNC uint32_t fio_queue_count(fio_queue_s *q);
```
Returns the number of pending tasks.
---
## Worker Threads
#### `fio_queue_workers_add`
```c
SFUNC int fio_queue_workers_add(fio_queue_s *q, size_t count);
```
Spawns `count` consumer threads that automatically perform tasks as they arrive. Threads sleep on a condition variable when idle.
**Returns:** `0` on success, `-1` on thread creation failure.
#### `fio_queue_workers_stop`
```c
SFUNC size_t fio_queue_workers_stop(fio_queue_s *q);
```
Signals all workers to stop. Returns immediately without waiting.
Pending tasks may remain in the queue (they can be performed later using `fio_queue_perform_all` or discarded by `fio_queue_destroy`).
**Returns:** the number of worker groups signaled (one per successful `fio_queue_workers_add` call still attached to the queue).
#### `fio_queue_workers_join`
```c
SFUNC void fio_queue_workers_join(fio_queue_s *q);
```
Signals workers to stop and blocks until they terminate.
If the queue had workers, any pending tasks are performed before the function returns (so `fio_queue_destroy`, which calls `fio_queue_workers_join`, drains a queue that has workers). A queue without workers is not drained: `fio_queue_destroy` on such a queue discards pending tasks without performing them.
#### `fio_queue_workers_wake`
```c
SFUNC void fio_queue_workers_wake(fio_queue_s *q);
```
Wakes all workers to check for new tasks. Called automatically on push.
---
## Timer Queue Types
#### `fio_timer_queue_s`
```c
typedef struct {
fio___timer_event_s *next;
FIO___LOCK_TYPE lock;
} fio_timer_queue_s;
```
Opaque timer queue.
#### `FIO_TIMER_QUEUE_INIT`
```c
#define FIO_TIMER_QUEUE_INIT /* ... */
```
Static initializer for a timer queue.
#### `fio_timer_schedule_args_s`
```c
typedef struct {
int (*fn)(void *, void *);
void *udata1;
void *udata2;
void (*on_finish)(void *, void *);
uint32_t every;
int32_t repetitions;
int64_t start_at;
} fio_timer_schedule_args_s;
```
Timer schedule arguments.
**Members:**
- `fn` — callback. Return non-zero to stop the timer.
- `udata1`, `udata2` — opaque data passed to `fn` and `on_finish`.
- `on_finish` — called when the timer stops.
- `every` — interval in milliseconds.
- `repetitions` — repeat count; `-1` means forever.
- `start_at` — base time in milliseconds; `0` uses `fio_time_milli()`.
---
## Timer Queue API
#### `fio_timer_schedule`
```c
SFUNC void fio_timer_schedule(fio_timer_queue_s *timer_queue,
fio_timer_schedule_args_s args);
#define fio_timer_schedule(timer_queue, ...) \
fio_timer_schedule((timer_queue), (fio_timer_schedule_args_s){__VA_ARGS__})
```
Adds a timed event to the timer queue. The macro accepts named arguments.
```c
fio_timer_schedule(&timers,
.fn = tick,
.udata1 = ctx,
.every = 1000,
.repetitions = -1);
```
#### `fio_timer_push2queue`
```c
SFUNC size_t fio_timer_push2queue(fio_queue_s *queue,
fio_timer_queue_s *timer_queue,
int64_t now_in_milliseconds);
```
Moves all due timer events into `queue`. Pass `0` for `now_in_milliseconds` to use `fio_time_milli()`.
**Returns:** number of timers pushed.
#### `fio_timer_next_at`
```c
FIO_IFUNC int64_t fio_timer_next_at(fio_timer_queue_s *timer_queue);
```
Returns the due time of the next event, or `INT64_MAX` if the queue is empty.
#### `fio_timer_destroy`
```c
SFUNC void fio_timer_destroy(fio_timer_queue_s *timer_queue);
```
Cancels all pending timers. Do not free the timer queue while timer tasks may still be queued in a `fio_queue_s`, because repeating timers reschedule themselves.
---
## Example
```c
#define FIO_QUEUE
#include "fio-stl.h"
void work(void *a, void *b) {
(void)a; (void)b;
fprintf(stderr, "working\n");
}
int tick(void *a, void *b) {
(void)a; (void)b;
fprintf(stderr, "tick\n");
return 0;
}
int main(void) {
fio_queue_s q = {0};
fio_queue_init(&q);
fio_queue_push(&q, .fn = work);
fio_queue_perform(&q);
fio_timer_queue_s t = FIO_TIMER_QUEUE_INIT;
fio_timer_schedule(&t, .fn = tick, .every = 100, .repetitions = 3);
fio_timer_push2queue(&q, &t, 0);
fio_queue_perform_all(&q);
fio_timer_destroy(&t);
fio_queue_destroy(&q);
return 0;
}
```
------------------------------------------------------------
# Packet Data Stream
```c
#define FIO_STREAM
#include "fio-stl.h"
```
A packet-based byte stream for buffering data from memory buffers and file descriptors. It is useful when writes are partial: add packets to the stream, read the next contiguous chunk into or from a scratch buffer, write what you can, then advance by the number of bytes written.
For the low-level helpers commonly used with streams, see [`004 files.md`](https://facil.io/0.8.x/file-helpers/), [`004 sock.md`](https://facil.io/0.8.x/basic-socket-helpers/), and [`102 poll api.md`](https://facil.io/0.8.x/posix-portable-polling-api/).
---
## Configuration Macros
#### `FIO_STREAM_COPY_PER_PACKET`
```c
#define FIO_STREAM_COPY_PER_PACKET 98304
```
Maximum copied payload per packet. Larger copied buffers are split into multiple packets so memory can be released progressively. Override before including the implementation.
#### `FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`
```c
#define FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN 116 /* 8 in DEBUG */
```
Small buffers are copied even when `copy_buffer == 0`, improving locality and avoiding tiny external references.
---
## Types
#### `fio_stream_s`
```c
typedef struct {
fio_stream_packet_s *next;
fio_stream_packet_s **pos;
size_t consumed;
size_t length;
} fio_stream_s;
```
Stream object. Treat the fields as private. Allocate on the stack with `FIO_STREAM_INIT` or on the heap with `fio_stream_new`.
#### `fio_stream_packet_s`
```c
typedef struct fio_stream_packet_s fio_stream_packet_s;
```
Opaque packed data node. Packets are prepared separately, then transferred to a stream with `fio_stream_add`.
---
## Lifecycle
#### `FIO_STREAM_INIT`
```c
#define FIO_STREAM_INIT(s) { .next = NULL, .pos = &(s).next }
```
Initializer for an in-place stream.
```c
fio_stream_s stream = FIO_STREAM_INIT(stream);
```
#### `fio_stream_new`
```c
fio_stream_s *fio_stream_new(void);
```
Allocates and initializes a heap stream.
**Returns:** stream pointer, or `NULL` on allocation failure.
Not declared when `FIO_REF_CONSTRUCTOR_ONLY` is defined.
#### `fio_stream_free`
```c
int fio_stream_free(fio_stream_s *stream);
```
Destroys `stream`, frees the stream object itself, and returns `0`.
Not declared when `FIO_REF_CONSTRUCTOR_ONLY` is defined.
#### `fio_stream_destroy`
```c
void fio_stream_destroy(fio_stream_s *stream);
```
Frees all queued packets and re-initializes the stream object. Safe to call with `NULL`.
---
## Packing Data
#### `fio_stream_pack_data`
```c
fio_stream_packet_s *fio_stream_pack_data(void *buf,
size_t len,
size_t offset,
uint8_t copy_buffer,
void (*dealloc_func)(void *));
```
Packs `len` bytes starting at `((char *)buf + offset)`.
- If `copy_buffer` is non-zero, or `len < FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`, bytes are copied into stream-owned packets.
- Otherwise the packet references `buf` and calls `dealloc_func(buf)` when the packet is freed, if `dealloc_func` is not `NULL`.
- Copied data larger than `FIO_STREAM_COPY_PER_PACKET` is split into multiple packets.
**Returns:** packet pointer, or `NULL` if `buf == NULL`, `len == 0`, the length is too large for the packet format, or allocation fails. If `dealloc_func` is provided, it is called on both success-after-copy and failure.
#### `fio_stream_pack_fd`
```c
fio_stream_packet_s *fio_stream_pack_fd(int fd,
size_t len,
size_t offset,
uint8_t keep_open);
```
Packs bytes from an open file descriptor. Reads are performed later by `fio_stream_read` using `fio_fd_read`.
- `len == 0` auto-detects the remaining file size with `fio_fd_size(fd)`.
- `offset` is the starting file offset.
- If `keep_open == 0`, the descriptor is closed when the packet is freed, or on pack failure after ownership was accepted.
- If `keep_open != 0`, the caller remains responsible for closing `fd`.
**Returns:** packet pointer, or `NULL` on invalid input, size detection failure / oversized auto-detected size when `len == 0`, or allocation failure.
#### `fio_stream_add`
```c
void fio_stream_add(fio_stream_s *stream, fio_stream_packet_s *packet);
```
Appends `packet` to `stream` and transfers packet ownership to the stream. If `stream` or `packet` is `NULL`, the packet is freed.
This is not thread-safe.
#### `fio_stream_pack_free`
```c
void fio_stream_pack_free(fio_stream_packet_s *packet);
```
Frees a packet that was not added to a stream. Do not call this after `fio_stream_add` succeeds.
---
## Reading and Consuming
#### `fio_stream_read`
```c
void fio_stream_read(fio_stream_s *stream, char **buf, size_t *len);
```
Reads the next available bytes without consuming them.
Before the call, `*buf` must point to a scratch buffer with at least `*len` bytes. This buffer is required when data spans multiple packets or comes from a file descriptor.
After the call:
- On empty stream or error, `*buf == NULL` and `*len == 0`.
- Otherwise `*buf` either still points to the supplied scratch buffer or points directly into stream-owned memory.
- `*len` is updated to the number of readable bytes available at `*buf`.
Reset both `*buf` and `*len` before each read if reusing the same scratch buffer. This is not thread-safe.
#### `fio_stream_advance`
```c
void fio_stream_advance(fio_stream_s *stream, size_t len);
```
Consumes `len` bytes from the stream, freeing fully consumed packets. Usually pass the number of bytes actually written to the destination.
This is not thread-safe.
#### `fio_stream_any`
```c
uint8_t fio_stream_any(fio_stream_s *stream);
```
Returns non-zero when `stream` has pending packets. Returns `0` for `NULL` or empty streams. This is not truly thread-safe.
#### `fio_stream_length`
```c
size_t fio_stream_length(fio_stream_s *stream);
```
Returns the number of bytes waiting in the stream. Call with a valid stream object. This is not truly thread-safe.
---
## Ownership and Thread-Safety
Packing creates a packet owned by the caller. `fio_stream_add` transfers that ownership to the stream. `fio_stream_destroy`, `fio_stream_free`, and `fio_stream_advance` free packets as needed.
For referenced memory packets, the referenced buffer must remain valid until the packet is freed. Pass `copy_buffer != 0` when that lifetime is not guaranteed. For file descriptor packets, keep the descriptor readable until the packet is consumed or destroyed; `keep_open` controls who closes it.
Packing packets can be done before taking the stream lock, but mutating or consuming a single `fio_stream_s` (`add`, `read`, `advance`, and length checks) should be externally synchronized when shared across threads.
---
## Example
```c
#define FIO_STREAM
#include "fio-stl.h"
#include
#include
int main(void) {
fio_stream_s stream = FIO_STREAM_INIT(stream);
char scratch[4096];
char msg[] = "hello, stream\n";
fio_stream_packet_s *p = fio_stream_pack_data(msg, strlen(msg), 0, 1, NULL);
fio_stream_add(&stream, p);
while (fio_stream_any(&stream)) {
char *buf = scratch;
size_t len = sizeof(scratch);
fio_stream_read(&stream, &buf, &len);
if (!len)
break;
size_t written = fwrite(buf, 1, len, stdout);
fio_stream_advance(&stream, written);
if (written != len)
break;
}
fio_stream_destroy(&stream);
return 0;
}
```
### File Descriptor Packet
```c
#define FIO_STREAM
#define FIO_FILES
#include "fio-stl.h"
fio_stream_s stream = FIO_STREAM_INIT(stream);
int fd = fio_filename_open("./asset.bin", O_RDONLY);
fio_stream_packet_s *p = fio_stream_pack_fd(fd, 0, 0, 0); /* stream closes fd */
fio_stream_add(&stream, p);
/* ... read / write packets ... */
fio_stream_destroy(&stream);
```
------------------------------------------------------------
# Markdown to HTML bstr Renderer
```c
#define FIO_MD2HTML
#include "fio-stl.h"
```
A complete-document Markdown / GFM renderer that turns parser events into escaped HTML stored in an owned `fio_bstr`. Markdown goes in, tags come out. Implemented in [`./103 md2html.h`](https://facil.io/0.8.x/api/103-md2html/).
Defining `FIO_MD2HTML` automatically includes:
- `FIO_STR` for `fio_bstr`
- `FIO_GFM` for Markdown / GFM parsing
### Error Codes
#### `FIO_MD2HTML_ERR_ALLOC`
```c
#ifndef FIO_MD2HTML_ERR_ALLOC
#define FIO_MD2HTML_ERR_ALLOC 1
#endif
```
Internal renderer error used when output allocation fails. Override before inclusion only if you need a different non-zero value.
### Rendering
#### `fio_md2html`
```c
SFUNC char *fio_md2html(char *bstr_target, fio_buf_info_s source);
```
Renders a complete Markdown / GFM document into a `fio_bstr`.
**Parameters:**
- `bstr_target` - existing `fio_bstr` to append to, or `NULL` to allocate a new one
- `source` - Markdown bytes to parse
**Returns:** the output `fio_bstr`, or `NULL` on parser / allocation failure.
**Ownership:** the returned buffer is owned by the caller and should be released with `fio_bstr_free`. If `bstr_target == NULL` and rendering fails, the newly allocated output is freed. If `bstr_target` was provided, ownership stays with the caller.
### Rendering Behavior
From the implementation:
- normal text, code, and attribute values are escaped
- raw Markdown HTML blocks / inline HTML are preserved, with GFM tagfilter applied
- blocked tag names include `iframe`, `noembed`, `noframes`, `plaintext`, `style`, `textarea`, `title`, and `xmp`; HTML blocks only filter `textarea` and `xmp`
- fenced code info strings use the first word as `class="language-..."`
- GFM tables render `...` and open ` ` for body rows
- table cell alignment uses `align="left"`, `align="right"`, or `align="center"`
- GFM task list items render disabled checkbox inputs
- autolinks add `mailto:` for bare email addresses and `http://` for `www.` links
- image alt text is escaped and strips nested link / emphasis markup
### Thread-Safety
The renderer uses caller-provided input and a local renderer state. It is thread-safe as long as separate calls do not mutate the same `bstr_target` at the same time.
### Example
```c
#define FIO_MD2HTML
#include "fio-stl.h"
#include
int main(void) {
char md[] = "# Demo\n\nHello **world**.\n";
char *html = fio_md2html(NULL, FIO_BUF_INFO1(md));
if (!html)
return 1;
fwrite(html, 1, fio_bstr_len(html), stdout);
fio_bstr_free(html);
return 0;
}
```
---
# Mustache-ish Template Engine
```c
#define FIO_MUSTACHE
#include "fio-stl.h"
```
A Mustache-ish template parser and renderer with variables, sections, inverted sections, partials, delimiter changes, comments, and optional YAML front matter callbacks. Logic-less-ish, because C callbacks still get a vote. Implemented in [`./104 mustache.h`](https://facil.io/0.8.x/api/104-mustache/).
Defining `FIO_MUSTACHE` pulls in string / `fio_bstr` helpers as needed.
### Configuration Macros
#### `FIO_MUSTACHE_MAX_DEPTH`
```c
#ifndef FIO_MUSTACHE_MAX_DEPTH
#define FIO_MUSTACHE_MAX_DEPTH 128
#endif
```
Maximum parser / builder nesting depth for sections and partials.
#### `FIO_MUSTACHE_PRESERVE_PADDING`
```c
#ifndef FIO_MUSTACHE_PRESERVE_PADDING
#define FIO_MUSTACHE_PRESERVE_PADDING 0
#endif
```
When enabled, preserves padding for stand-alone variables and partial templates.
#### `FIO_MUSTACHE_LAMBDA_SUPPORT`
```c
#ifndef FIO_MUSTACHE_LAMBDA_SUPPORT
#define FIO_MUSTACHE_LAMBDA_SUPPORT 0
#endif
```
When enabled, stores raw section text for lambda-style section handling through `is_lambda`.
#### `FIO_MUSTACHE_ISOLATE_PARTIALS`
```c
#ifndef FIO_MUSTACHE_ISOLATE_PARTIALS
#define FIO_MUSTACHE_ISOLATE_PARTIALS 1
#endif
```
When enabled, limits partial lookup scope to the context of the partial's section.
#### `FIO_MUSTACHE_SECURE_PATH`
```c
#ifndef FIO_MUSTACHE_SECURE_PATH
#define FIO_MUSTACHE_SECURE_PATH 1
#endif
```
When enabled, skips partial names that attempt `../` path traversal.
### Syntax Quick Map
| Syntax | Meaning |
| --- | --- |
| `{{name}}` | escaped variable |
| `{{{name}}}` | raw variable |
| `{{&name}}` | raw variable |
| `{{#items}}...{{/items}}` | truthy section / array iteration |
| `{{^items}}...{{/items}}` | inverted section |
| `{{>partial}}` | partial template include |
| `{{! comment}}` | comment |
| `{{=<% %>=}}` | delimiter change |
| `{{.}}` | current context |
Dot lookup such as `person.name` is supported by repeatedly calling `get_var` for path segments.
### Types
#### `fio_mustache_s`
```c
typedef struct fio_mustache_s fio_mustache_s;
```
Opaque parsed template object. Internally it is stored as a `fio_bstr` instruction stream.
**Ownership:** `fio_mustache_load` returns an owned template. Free it with `fio_mustache_free`. `fio_mustache_dup` creates a copy; it is not a shared reference counter in this header.
#### `fio_mustache_bargs_s`
```c
typedef struct fio_mustache_bargs_s fio_mustache_bargs_s;
```
Forward declaration for build arguments. The full struct is listed below.
#### `fio_mustache_load_args_s`
```c
typedef struct {
fio_buf_info_s data;
fio_buf_info_s filename;
fio_buf_info_s (*load_file_data)(fio_buf_info_s filename, void *udata);
void (*free_file_data)(fio_buf_info_s file_data, void *udata);
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
void *udata;
} fio_mustache_load_args_s;
```
Load / parse settings.
**Members:**
- `data` - preloaded template bytes
- `filename` - file name, also used as base path for partials
- `load_file_data` - callback that loads file contents
- `free_file_data` - callback that frees loaded file contents
- `on_yaml_front_matter` - called when front matter is found
- `udata` - user data for load callbacks
If both `load_file_data` and `free_file_data` are missing, defaults use `fio_bstr_readfile` and `fio_bstr_free`. If one is custom, provide the matching cleanup. Either `filename` or `data` is required.
#### `fio_mustache_bargs_s`
```c
struct fio_mustache_bargs_s {
void *(*write_text)(void *udata, fio_buf_info_s txt);
void *(*write_text_escaped)(void *udata, fio_buf_info_s raw);
void *(*get_var)(void *ctx, fio_buf_info_s name);
size_t (*array_length)(void *ctx);
void *(*get_var_index)(void *ctx, size_t index);
fio_buf_info_s (*var2str)(void *var);
int (*var_is_truthful)(void *ctx);
void (*release_var)(void *ctx);
int (*is_lambda)(void **udata,
void *ctx,
fio_buf_info_s raw_template_section);
void *ctx;
void *udata;
};
```
Build / render callbacks.
**Members:**
- `write_text` - writes raw template text
- `write_text_escaped` - writes escaped variable text
- `get_var` - returns a value for `name` from `ctx`
- `array_length` - returns array length, or `0` for non-array values
- `get_var_index` - returns an item context by index
- `var2str` - returns a string view for a value
- `var_is_truthful` - returns non-zero for truthy values
- `release_var` - releases values returned by callbacks
- `is_lambda` - handles lambda sections when lambda support is enabled
- `ctx` - root render context
- `udata` - output / user pointer; the final value is returned by `fio_mustache_build`
If both writer callbacks are missing, defaults append to a `fio_bstr` in `udata`. The returned pointer should be freed with `fio_bstr_free`.
### Loading
#### `fio_mustache_load`
```c
SFUNC fio_mustache_s *fio_mustache_load(fio_mustache_load_args_s settings);
#define fio_mustache_load(...) \
fio_mustache_load((fio_mustache_load_args_s){__VA_ARGS__})
```
Loads and parses a template from `settings.data` or `settings.filename`.
If `filename` is provided and `data` is empty, the loader callback is used. If both are provided, `data` is parsed and `filename` is kept for partial path resolution.
**Returns:** a parsed template object, or `NULL` on error.
#### `fio_mustache_free`
```c
SFUNC void fio_mustache_free(fio_mustache_s *m);
```
Frees a parsed template. Accepts `NULL`.
#### `fio_mustache_dup`
```c
SFUNC fio_mustache_s *fio_mustache_dup(fio_mustache_s *m);
```
Copies a parsed template using `fio_bstr_copy`.
**Returns:** the copied template, or `NULL` if `m == NULL`.
### Rendering
#### `fio_mustache_build`
```c
SFUNC void *fio_mustache_build(fio_mustache_s *m, fio_mustache_bargs_s);
#define fio_mustache_build(m, ...) \
fio_mustache_build((m), ((fio_mustache_bargs_s){__VA_ARGS__}))
```
Renders `m` with the supplied context and callbacks.
**Returns:** the final `udata` value. If `m == NULL`, returns `args.udata` unchanged.
During rendering:
- escaped variables call `write_text_escaped`
- raw variables call `write_text`
- sections render once for truthy non-arrays, or once per array item
- inverted sections render when `var_is_truthful` returns zero
- values returned from lookup / array callbacks are released with `release_var`
### Partials and Files
Partials are loaded by name through `load_file_data`. The implementation tries each name with these suffixes:
1. `.mustache`
2. `.html`
3. no suffix
Relative partials are searched against the including template's path chain, then as provided. With `FIO_MUSTACHE_SECURE_PATH`, `../` partials are skipped.
### YAML Front Matter
If a template begins with `---` followed by a line break, the parser scans until a closing `---` line and calls:
```c
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
```
The front matter bytes are not rendered. The engine does not parse YAML for you. It has enough hobbies.
### Thread-Safety
Parsed templates are immutable during `fio_mustache_build`, so separate builds can use the same template as long as your callbacks and contexts are safe. Loading and freeing are caller-owned operations.
### Example
```c
#define FIO_MUSTACHE
#include "fio-stl.h"
#include
#include
typedef struct {
const char *name;
} data_s;
static void *get_var(void *ctx, fio_buf_info_s name) {
data_s *d = (data_s *)ctx;
if (name.len == 4 && !memcmp(name.buf, "name", 4))
return (void *)d->name;
return NULL;
}
static fio_buf_info_s var2str(void *var) {
char *s = (char *)var;
return FIO_BUF_INFO2(s, strlen(s));
}
int main(void) {
char template_text[] = "Hello, {{name}}!\n";
data_s data = {.name = "World"};
fio_mustache_s *m = fio_mustache_load(
.data = FIO_BUF_INFO1(template_text));
if (!m)
return 1;
char *out = fio_mustache_build(m,
.ctx = &data,
.get_var = get_var,
.var2str = var2str);
if (out) {
fwrite(out, 1, fio_bstr_len(out), stdout);
fio_bstr_free(out);
}
fio_mustache_free(m);
return 0;
}
```
---
# Crypto Core
```c
#define FIO_CRYPTO_CORE
#include "fio-stl.h"
```
The crypto slice is a small, zero-dependency toolbox for code that needs to hash, sign, encrypt, parse certificates, or derive keys without dragging in a larger library. Handy? yes. Magic security dust? no.
`FIO_CRYPTO_CORE` itself is the shared glue. It defines the AEAD function pointer types used by the symmetric ciphers and by higher-level helpers that accept “any AEAD with this call shape”. Most users include a specific module, or define `FIO_CRYPTO` to pull in the crypto set.
## Map of the Crypto Modules
| Class | Docs | What lives there |
| --- | --- | --- |
| Hash | [SHA-1](https://facil.io/0.8.x/sha-1/), [SHA-2](https://facil.io/0.8.x/sha-1/), [SHA-3 / SHAKE](https://facil.io/0.8.x/sha-1/), [BLAKE2](https://facil.io/0.8.x/blake2/) | Digest functions, streaming hash contexts, SHA/BLAKE HMAC helpers where provided. SHA-1 is legacy-only glue. |
| Symmetric | [ChaCha20-Poly1305](https://facil.io/0.8.x/chacha20-poly1305/), [AES-GCM](https://facil.io/0.8.x/aes-gcm/) | AEAD encryption, stream cipher helpers, and the shared in-place authenticated encryption shape. |
| Asymmetric | [Ed25519 & X25519](https://facil.io/0.8.x/ed25519-x25519/), [P-256](https://facil.io/0.8.x/p-256-secp256r1/), [P-384](https://facil.io/0.8.x/p-256-secp256r1/), [RSA](https://facil.io/0.8.x/rsa-signatures/) | Signatures, key exchange, ECIES-style X25519 encryption, and RSA signatures for TLS-style use cases. |
| PKI | [ASN.1 DER](https://facil.io/0.8.x/asn-1-der/), [X.509](https://facil.io/0.8.x/http-1-x-parser/), [PEM](https://facil.io/0.8.x/p-256-secp256r1/) | DER/PEM parsing, certificate fields, hostname checks, signature checks, and certificate chain validation helpers. |
| KDF | [HKDF](https://facil.io/0.8.x/hkdf/), [Argon2](https://facil.io/0.8.x/argon2/), [Lyra2](https://facil.io/0.8.x/lyra2/), [OTP](https://facil.io/0.8.x/otp-totp/), [Secrets](https://facil.io/0.8.x/secrets/) | Key derivation, password hashing, TOTP codes, and hashing a process secret into a stable internal value. |
| Post-Quantum | [ML-KEM-768](https://facil.io/0.8.x/ml-entity-decoding/) | ML-KEM-768 key encapsulation and the X25519MLKEM768 hybrid key exchange shape used by TLS drafts. |
## The Shared AEAD Shape
The core header defines two function pointer types:
```c
typedef void(fio_crypto_enc_fn)(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
typedef int(fio_crypto_dec_fn)(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Current AEAD implementations that match this shape include:
- `fio_chacha20_poly1305_enc` / `fio_chacha20_poly1305_dec`
- `fio_xchacha20_poly1305_enc` / `fio_xchacha20_poly1305_dec`
- `fio_aes128_gcm_enc` / `fio_aes128_gcm_dec`
- `fio_aes256_gcm_enc` / `fio_aes256_gcm_dec`
These functions encrypt or decrypt `data` in place. `ad` is authenticated but not encrypted. `mac` is the authentication tag buffer; the current AEAD modules use 16-byte tags. Key and nonce sizes belong to the selected cipher, not to the function pointer type.
Decryption returns `0` when authentication succeeds and `-1` when it fails. Treat any failure as “message not trusted”; do not parse, log in detail, or partially use the plaintext.
## Safe Use, Plainly
This module helps when you need portable crypto building blocks inside the STL: hashing buffers, deriving session keys, signing messages, checking certificates, or doing AEAD encryption with a known protocol design.
It does **not** promise that your protocol is safe. It does not manage long-term keys for you, pick nonces, rotate secrets, maintain a root trust store, protect keys in hardware, provide FIPS validation, or substitute for an external security review. Several public-key and PKI headers explicitly note that they have not been independently audited.
Use this toolbox when:
- you need the zero-dependency facil.io STL path;
- the protocol is already designed and reviewed;
- inputs, key sizes, nonce rules, and failure paths are controlled;
- a compact embedded copy is more important than delegating to a platform stack.
Prefer audited platform/security libraries such as OpenSSL, BoringSSL, libsodium, CommonCrypto, platform TLS, or OS key stores when:
- you are building TLS, PKI, payment, identity, or compliance-sensitive systems;
- private keys live for a long time or leave the process boundary;
- certificate trust decisions must track OS/browser policy;
- side-channel hardening, hardware acceleration policy, or formal validation matters.
In short: this is a sharp little knife. Useful. Keep fingers clear.
## Implementation Notes
The crypto headers use optional CPU-specific paths where available, including SHA intrinsics, AES-NI/PCLMULQDQ, ARM crypto extensions, NEON, and AVX2. The public APIs stay the same when the implementation falls back to portable C.
The SIMD notes in `150 crypto core.h` are documentation for maintainers: ChaCha20-Poly1305 can process blocks in parallel, Curve25519 uses vectorized field add/sub/cswap where useful, and ML-KEM vectorizes NTT/reduction paths. These are implementation details, not extra guarantees.
# BLAKE2
```c
#define FIO_BLAKE2
#include "fio-stl.h"
```
BLAKE2b (64-bit, up to 64-byte digest) and BLAKE2s (32-bit, up to 32-byte digest). Both are fast, modern cryptographic hashes with built-in keyed hashing, standard HMAC wrappers, and a streaming init/consume/finalize API.
### Types
#### `fio_blake2b_s`
```c
typedef struct {
uint64_t h[8]; /* state */
uint64_t t[2]; /* total bytes processed (128-bit counter) */
uint64_t f[2]; /* finalization flags */
uint8_t buf[128]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2b_s;
```
Streaming BLAKE2b context. Treat it as opaque and initialize with `fio_blake2b_init`.
#### `fio_blake2s_s`
```c
typedef struct {
uint32_t h[8]; /* state */
uint32_t t[2]; /* total bytes processed (64-bit counter) */
uint32_t f[2]; /* finalization flags */
uint8_t buf[64]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2s_s;
```
Streaming BLAKE2s context. Treat it as opaque and initialize with `fio_blake2s_init`.
### BLAKE2b Functions
#### `fio_blake2b`
```c
FIO_IFUNC fio_u512 fio_blake2b(const void *data, uint64_t len);
```
One-shot BLAKE2b with the full 64-byte digest.
**Parameters:**
- `data` — bytes to hash.
- `len` — number of bytes.
**Returns:** the 64-byte digest as `fio_u512`.
**Note:** signature matches `fio_sha512`, so the two are interchangeable as `fio_u512 (*)(const void *, uint64_t)`.
#### `fio_blake2b_hash`
```c
SFUNC void fio_blake2b_hash(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len,
const void *restrict key,
size_t keylen);
```
Flexible-output BLAKE2b with optional keyed hashing.
**Parameters:**
- `out` — destination buffer (at least `outlen` bytes).
- `outlen` — digest length (1–64; defaults to 64 if 0).
- `data` — bytes to hash.
- `len` — data length.
- `key` — optional secret key (NULL for unkeyed).
- `keylen` — key length (0–64; clamped to 64 if larger).
**Note:** keyed BLAKE2b is a built-in MAC mode; for RFC-2104 HMAC, use `fio_blake2b_hmac`.
#### `fio_blake2b_hmac`
```c
SFUNC fio_u512 fio_blake2b_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Standard HMAC-BLAKE2b with a 64-byte digest.
**Parameters:**
- `key` — secret key.
- `key_len` — key length.
- `msg` — message to authenticate.
- `msg_len` — message length.
**Returns:** 64-byte authentication code as `fio_u512`.
#### `fio_blake2b_init`
```c
SFUNC fio_blake2b_s fio_blake2b_init(size_t outlen,
const void *key,
size_t keylen);
```
Initializes a BLAKE2b streaming context.
**Parameters:**
- `outlen` — desired digest length (1–64; defaults to 64 if 0).
- `key` — optional secret key (NULL for unkeyed).
- `keylen` — key length (0–64; clamped to 64 if larger).
#### `fio_blake2b_consume`
```c
SFUNC void fio_blake2b_consume(fio_blake2b_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds more data into a BLAKE2b streaming context. May be called repeatedly.
#### `fio_blake2b_finalize`
```c
SFUNC fio_u512 fio_blake2b_finalize(fio_blake2b_s *h);
```
Finalizes the stream and returns the digest.
**Returns:** digest as `fio_u512`. Only the first `outlen` bytes are valid.
### BLAKE2s Functions
#### `fio_blake2s`
```c
FIO_IFUNC fio_u256 fio_blake2s(const void *data, uint64_t len);
```
One-shot BLAKE2s with the full 32-byte digest.
#### `fio_blake2s_hash`
```c
SFUNC void fio_blake2s_hash(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len,
const void *restrict key,
size_t keylen);
```
Flexible-output BLAKE2s with optional keyed hashing. `outlen` is 1–32 (defaults to 32 if 0).
#### `fio_blake2s_hmac`
```c
SFUNC fio_u256 fio_blake2s_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Standard HMAC-BLAKE2s with a 32-byte digest.
#### `fio_blake2s_init`
```c
SFUNC fio_blake2s_s fio_blake2s_init(size_t outlen,
const void *key,
size_t keylen);
```
Initializes a BLAKE2s streaming context. `outlen` is 1–32 (defaults to 32 if 0).
#### `fio_blake2s_consume`
```c
SFUNC void fio_blake2s_consume(fio_blake2s_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds more data into a BLAKE2s streaming context.
#### `fio_blake2s_finalize`
```c
SFUNC fio_u256 fio_blake2s_finalize(fio_blake2s_s *h);
```
Finalizes the stream and returns the digest.
### Examples
```c
#define FIO_BLAKE2
#include "fio-stl.h"
void examples(void) {
const char *msg = "Hello, BLAKE2!";
/* One-shot */
fio_u512 b2b = fio_blake2b(msg, strlen(msg));
fio_u256 b2s = fio_blake2s(msg, strlen(msg));
/* Flexible output with key */
uint8_t digest[16];
fio_blake2b_hash(digest, 16, msg, strlen(msg), "key", 3);
/* Streaming */
fio_blake2b_s ctx = fio_blake2b_init(64, NULL, 0);
fio_blake2b_consume(&ctx, "Hello, ", 7);
fio_blake2b_consume(&ctx, "BLAKE2!", 7);
fio_u512 result = fio_blake2b_finalize(&ctx);
/* HMAC */
fio_u512 mac = fio_blake2b_hmac("key", 3, msg, strlen(msg));
}
```
### BLAKE2b vs BLAKE2s
| Property | BLAKE2b | BLAKE2s |
|---|---|---|
| Word size | 64-bit | 32-bit |
| Block size | 128 bytes | 64 bytes |
| Max digest | 64 bytes | 32 bytes |
| Max key | 64 bytes | 32 bytes |
| Rounds | 12 | 10 |
| Result type | `fio_u512` | `fio_u256` |
------------------------------------------------------------
# ChaCha20 & Poly1305
```c
#define FIO_CHACHA
#include "fio-stl.h"
```
ChaCha20 stream cipher, Poly1305 authenticator, and the ChaCha20-Poly1305 AEAD combination. Also includes XChaCha20-Poly1305 with a 192-bit nonce, which is safer for random nonces than the 96-bit variant.
**Security note:** this implementation has not been independently audited. Use at your own risk, and prefer a tested cryptographic library when one is available.
### ChaCha20-Poly1305 API
#### `fio_chacha20_poly1305_enc`
```c
SFUNC void fio_chacha20_poly1305_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
In-place encryption with a 16-byte Poly1305 tag.
**Parameters:**
- `mac` — output buffer; must have at least 16 writable bytes.
- `data` — plaintext buffer; encrypted in place.
- `len` — data length.
- `ad` — additional authenticated data (not encrypted); may be `NULL`.
- `adlen` — length of `ad`.
- `key` — 32-byte key.
- `nonce` — 12-byte nonce.
#### `fio_chacha20_poly1305_dec`
```c
SFUNC int fio_chacha20_poly1305_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
In-place decryption with tag verification.
**Returns:** `0` on success, `-1` if authentication fails. On failure `data` is zeroed.
#### `fio_chacha20_poly1305_auth`
```c
SFUNC void fio_chacha20_poly1305_auth(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Computes the Poly1305 tag for already-encrypted data without decrypting it.
### Standalone ChaCha20 / Poly1305
#### `fio_chacha20`
```c
SFUNC void fio_chacha20(void *restrict data,
size_t len,
const void *key,
const void *nonce,
uint32_t counter);
```
In-place ChaCha20 encryption or decryption.
**Parameters:**
- `data` — buffer to transform in place.
- `len` — buffer length.
- `key` — 32-byte key.
- `nonce` — 12-byte nonce.
- `counter` — block counter; usually `1` unless resuming mid-ciphertext.
#### `fio_poly1305_auth`
```c
SFUNC void fio_poly1305_auth(void *restrict mac_dest,
void *restrict message,
size_t len,
const void *ad,
size_t ad_len,
const void *key256bits);
```
Computes a Poly1305 MAC for `message` and `ad` using a 32-byte key.
**Parameters:**
- `mac_dest` — output buffer; must have at least 16 writable bytes.
- `message` — message to authenticate; may be `NULL` if `len` is 0.
- `len` — message length.
- `ad` — additional data; may be `NULL`.
- `ad_len` — additional data length.
- `key256bits` — 32-byte Poly1305 key.
### XChaCha20-Poly1305 API
XChaCha20 uses a 24-byte nonce, making random nonces safe from birthday-bound collisions.
#### `fio_xchacha20_poly1305_enc`
```c
SFUNC void fio_xchacha20_poly1305_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same shape as `fio_chacha20_poly1305_enc`, but `nonce` must be 24 bytes.
#### `fio_xchacha20_poly1305_dec`
```c
SFUNC int fio_xchacha20_poly1305_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same shape as `fio_chacha20_poly1305_dec`, but `nonce` must be 24 bytes.
#### `fio_xchacha20`
```c
SFUNC void fio_xchacha20(void *restrict data,
size_t len,
const void *key,
const void *nonce,
uint32_t counter);
```
In-place XChaCha20 encryption or decryption.
### Example
```c
#define FIO_CHACHA
#include "fio-stl.h"
int main(void) {
uint8_t key[32] = {0};
uint8_t nonce[12] = {0};
uint8_t msg[32] = "hello, chacha world!";
uint8_t tag[16];
fio_rand_bytes_secure(key, sizeof(key));
fio_rand_bytes_secure(nonce, sizeof(nonce));
fio_chacha20_poly1305_enc(tag, msg, sizeof(msg), NULL, 0, key, nonce);
if (fio_chacha20_poly1305_dec(tag, msg, sizeof(msg), NULL, 0, key, nonce)) {
printf("authentication failed\n");
return 1;
}
return 0;
}
```
------------------------------------------------------------
# HKDF
```c
#define FIO_HKDF
#include "fio-stl.h"
```
HMAC-based Key Derivation Function per [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869). HKDF extracts entropy from input keying material into a pseudorandom key, then expands that key into one or more output keys.
Supports SHA-256 and SHA-384 as the underlying HMAC.
**Note:** HKDF needs `fio_sha256_hmac` and `fio_sha512_hmac`. Define `FIO_SHA2` before `FIO_HKDF`, or use `FIO_CRYPTO` to pull in everything.
### Constants
#### `FIO_HKDF_SHA256_HASH_LEN`
```c
#define FIO_HKDF_SHA256_HASH_LEN 32
```
SHA-256 hash length in bytes.
#### `FIO_HKDF_SHA384_HASH_LEN`
```c
#define FIO_HKDF_SHA384_HASH_LEN 48
```
SHA-384 hash length in bytes.
### Functions
#### `fio_hkdf_extract`
```c
SFUNC void fio_hkdf_extract(void *restrict prk,
const void *restrict salt,
size_t salt_len,
const void *restrict ikm,
size_t ikm_len,
int use_sha384);
```
HKDF-Extract: `PRK = HMAC-Hash(salt, IKM)`.
**Parameters:**
- `prk` — output buffer (32 bytes for SHA-256, 48 for SHA-384).
- `salt` — optional salt; if `NULL`, a zero string of hash length is used.
- `salt_len` — salt length.
- `ikm` — input keying material.
- `ikm_len` — IKM length.
- `use_sha384` — non-zero for SHA-384, zero for SHA-256.
#### `fio_hkdf_expand`
```c
SFUNC void fio_hkdf_expand(void *restrict okm,
size_t okm_len,
const void *restrict prk,
size_t prk_len,
const void *restrict info,
size_t info_len,
int use_sha384);
```
HKDF-Expand: `OKM = HKDF-Expand(PRK, info, L)`.
**Parameters:**
- `okm` — output buffer.
- `okm_len` — desired output length (max `255 * hash_len`).
- `prk` — pseudorandom key from Extract.
- `prk_len` — 32 for SHA-256, 48 for SHA-384.
- `info` — optional context/info string; may be `NULL`.
- `info_len` — info length.
- `use_sha384` — non-zero for SHA-384, zero for SHA-256.
#### `fio_hkdf`
```c
SFUNC void fio_hkdf(void *restrict okm,
size_t okm_len,
const void *restrict salt,
size_t salt_len,
const void *restrict ikm,
size_t ikm_len,
const void *restrict info,
size_t info_len,
int use_sha384);
```
Combined HKDF: Extract followed by Expand in one call.
### Example
```c
#define FIO_SHA2
#define FIO_HKDF
#include "fio-stl.h"
int main(void) {
const char *ikm = "input keying material";
const char *salt = "salt";
const char *info = "app context";
uint8_t key[32];
fio_hkdf(key, sizeof(key), salt, strlen(salt),
ikm, strlen(ikm), info, strlen(info), 0);
return 0;
}
```
------------------------------------------------------------
# SHA-1
```c
#define FIO_SHA1
#include "fio-stl.h"
```
SHA-1 hashing and HMAC. It is cryptographically broken, so do not use it for anything that needs security. Some protocols (hello, WebSockets) still ask for it, so it lives here for compatibility.
### Types
#### `fio_sha1_s`
```c
typedef union {
#ifdef __SIZEOF_INT128__
__uint128_t align__;
#else
uint64_t align__;
#endif
uint32_t v[5];
uint8_t digest[20];
} fio_sha1_s;
```
Container for the 20-byte SHA-1 digest.
**Members:**
- `align__` - alignment padding.
- `v` - the 5 × 32-bit state words.
- `digest` - the 20-byte digest as raw bytes.
### API Functions
#### `fio_sha1`
```c
fio_sha1_s fio_sha1(const void *data, uint64_t len);
```
One-shot SHA-1 hash of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_sha1_s` containing the 20-byte digest.
#### `fio_sha1_hmac`
```c
fio_sha1_s fio_sha1_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA1, producing a 20-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_sha1_s` containing the 20-byte HMAC.
**Note:** keys longer than 64 bytes are first hashed with SHA-1.
#### `fio_sha1_len`
```c
size_t fio_sha1_len(void);
```
**Returns:** the SHA-1 digest length in bytes (20).
#### `fio_sha1_digest`
```c
uint8_t *fio_sha1_digest(fio_sha1_s *s);
```
**Parameters:**
- `s` - pointer to a `fio_sha1_s` result.
**Returns:** a pointer to the 20-byte digest inside `s`.
### Examples
#### One-shot hash
```c
#define FIO_SHA1
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
fio_sha1_s r = fio_sha1(msg, strlen(msg));
for (size_t i = 0; i < fio_sha1_len(); ++i)
printf("%02x", fio_sha1_digest(&r)[i]);
printf("\n");
return 0;
}
```
#### HMAC
```c
fio_sha1_s mac = fio_sha1_hmac("secret", 6, "hello", 5);
```
**Note:** SHA-1 is broken. Prefer SHA-256, SHA-3, or another modern hash for security-sensitive work.
------------------------------------------------------------
# SHA-2
```c
#define FIO_SHA2
#include "fio-stl.h"
```
SHA-256 and SHA-512 hashing, with both one-shot and streaming APIs plus HMAC. These are the workhorse hashes for checksums, signatures, and anything that needs a collision-resistant digest.
### Types
#### `fio_sha256_s`
```c
typedef struct {
fio_u256 hash;
fio_u512 cache;
uint64_t total_len;
} fio_sha256_s;
```
Streaming SHA-256 state. Initialize with `fio_sha256_init`, feed with `fio_sha256_consume`, and finish with `fio_sha256_finalize`.
#### `fio_sha512_s`
```c
typedef struct {
fio_u512 hash;
fio_u1024 cache;
uint64_t total_len;
} fio_sha512_s;
```
Streaming SHA-512 state. Initialize with `fio_sha512_init`, feed with `fio_sha512_consume`, and finish with `fio_sha512_finalize`.
### SHA-256 Functions
#### `fio_sha256`
```c
fio_u256 fio_sha256(const void *data, uint64_t len);
```
One-shot SHA-256 of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_u256` containing the 32-byte digest.
#### `fio_sha256_init`
```c
fio_sha256_s fio_sha256_init(void);
```
**Returns:** a freshly initialized SHA-256 streaming state.
#### `fio_sha256_consume`
```c
void fio_sha256_consume(fio_sha256_s *h, const void *data, uint64_t len);
```
Feeds more data into a streaming SHA-256 hash. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming state.
- `data` - pointer to the data to add.
- `len` - length of the data in bytes.
#### `fio_sha256_finalize`
```c
fio_u256 fio_sha256_finalize(fio_sha256_s *h);
```
Finalizes the hash and returns the 32-byte digest.
**Parameters:**
- `h` - pointer to the streaming state.
**Returns:** a `fio_u256` containing the digest.
**Note:** after finalization, `h` should not be reused without re-initialization.
### SHA-512 Functions
#### `fio_sha512`
```c
fio_u512 fio_sha512(const void *data, uint64_t len);
```
One-shot SHA-512 of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_u512` containing the 64-byte digest.
#### `fio_sha512_init`
```c
fio_sha512_s fio_sha512_init(void);
```
**Returns:** a freshly initialized SHA-512 streaming state.
#### `fio_sha512_consume`
```c
void fio_sha512_consume(fio_sha512_s *h, const void *data, uint64_t len);
```
Feeds more data into a streaming SHA-512 hash. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming state.
- `data` - pointer to the data to add.
- `len` - length of the data in bytes.
#### `fio_sha512_finalize`
```c
fio_u512 fio_sha512_finalize(fio_sha512_s *h);
```
Finalizes the hash and returns the 64-byte digest.
**Parameters:**
- `h` - pointer to the streaming state.
**Returns:** a `fio_u512` containing the digest.
**Note:** after finalization, `h` should not be reused without re-initialization.
### SHA-384 Functions
SHA-384 is SHA-512 with different initial values, truncated to 48 bytes
(truncating a SHA-512 *result* is NOT the same as SHA-384).
#### `fio_sha384`
```c
fio_u512 fio_sha384(const void *data, uint64_t len);
```
Computes SHA-384 in a single call.
**Returns:** a `fio_u512` whose first 48 bytes hold the digest.
#### `fio_sha384_init` / `fio_sha384_consume` / `fio_sha384_finalize`
```c
fio_sha512_s fio_sha384_init(void);
void fio_sha384_consume(fio_sha512_s *h, const void *data, uint64_t len);
fio_u512 fio_sha384_finalize(fio_sha512_s *h);
```
Streaming SHA-384: same state type as SHA-512; `fio_sha384_init` seeds the
SHA-384 initial values, consume/finalize alias the SHA-512 operations.
### HMAC Functions
#### `fio_sha256_hmac`
```c
fio_u256 fio_sha256_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA256, producing a 32-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_u256` containing the 32-byte HMAC.
**Note:** keys longer than 64 bytes are hashed first.
#### `fio_sha512_hmac`
```c
fio_u512 fio_sha512_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA512, producing a 64-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_u512` containing the 64-byte HMAC.
**Note:** keys longer than 128 bytes are hashed first.
### Examples
#### One-shot SHA-256
```c
#define FIO_SHA2
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
fio_u256 r = fio_sha256(msg, strlen(msg));
for (size_t i = 0; i < sizeof(r.u8); ++i)
printf("%02x", r.u8[i]);
printf("\n");
return 0;
}
```
#### Streaming SHA-256
```c
fio_sha256_s h = fio_sha256_init();
fio_sha256_consume(&h, "hello, ", 7);
fio_sha256_consume(&h, "world", 5);
fio_u256 r = fio_sha256_finalize(&h);
```
#### HMAC-SHA256
```c
fio_u256 mac = fio_sha256_hmac("secret", 6, "hello", 5);
```
**Note:** all state lives in caller-owned variables. The functions do not allocate heap memory and are thread-safe as long as each thread uses its own state.
------------------------------------------------------------
# SHA-3
```c
#define FIO_SHA3
#include "fio-stl.h"
```
The SHA-3 family (SHA3-224/256/384/512) and the SHAKE128/SHAKE256 extendable-output functions, built on Keccak-f[1600]. All variants share one streaming context type.
### Types
#### `fio_sha3_s`
```c
typedef struct {
uint64_t state[25];
uint8_t buf[200];
size_t buflen;
size_t rate;
size_t outlen;
uint8_t delim;
} fio_sha3_s;
```
Streaming context for every SHA-3 and SHAKE variant.
**Members:**
- `state` - the 25-word Keccak-f[1600] state.
- `buf` - partial-block input buffer.
- `buflen` - bytes currently buffered.
- `rate` - absorption/squeezing rate in bytes, set by the init function.
- `outlen` - fixed output length in bytes for SHA-3; 0 for SHAKE.
- `delim` - domain separator (`0x06` for SHA-3, `0x1F` for SHAKE).
**Note:** treat this as opaque. Always initialize it with one of the `fio_sha3_*_init` or `fio_shake*_init` functions.
### SHA-3 Fixed-Output Functions
#### `fio_sha3_224_init`
```c
fio_sha3_s fio_sha3_224_init(void);
```
**Returns:** a SHA3-224 context (28-byte output, rate 144).
#### `fio_sha3_256_init`
```c
fio_sha3_s fio_sha3_256_init(void);
```
**Returns:** a SHA3-256 context (32-byte output, rate 136).
#### `fio_sha3_384_init`
```c
fio_sha3_s fio_sha3_384_init(void);
```
**Returns:** a SHA3-384 context (48-byte output, rate 104).
#### `fio_sha3_512_init`
```c
fio_sha3_s fio_sha3_512_init(void);
```
**Returns:** a SHA3-512 context (64-byte output, rate 72).
#### `fio_sha3_consume`
```c
void fio_sha3_consume(fio_sha3_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds `len` bytes into a SHA-3 context. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming context.
- `data` - pointer to the data to absorb.
- `len` - length of the data in bytes.
#### `fio_sha3_finalize`
```c
void fio_sha3_finalize(fio_sha3_s *restrict h, void *restrict out);
```
Pads, permutes, and writes `h->outlen` digest bytes to `out`.
**Parameters:**
- `h` - pointer to the streaming context.
- `out` - output buffer, must hold at least `h->outlen` bytes.
**Note:** do not call this on a SHAKE context. Use `fio_shake_squeeze` instead.
#### `fio_sha3_224`
```c
void fio_sha3_224(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-224.
**Parameters:**
- `out` - output buffer (28 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_256`
```c
void fio_sha3_256(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-256.
**Parameters:**
- `out` - output buffer (32 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_384`
```c
void fio_sha3_384(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-384.
**Parameters:**
- `out` - output buffer (48 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_512`
```c
void fio_sha3_512(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-512.
**Parameters:**
- `out` - output buffer (64 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
### SHAKE Extendable-Output Functions
#### `fio_shake128_init`
```c
fio_sha3_s fio_shake128_init(void);
```
**Returns:** a SHAKE128 context (variable output, rate 168).
#### `fio_shake256_init`
```c
fio_sha3_s fio_shake256_init(void);
```
**Returns:** a SHAKE256 context (variable output, rate 136).
#### `fio_shake_consume`
```c
#define fio_shake_consume fio_sha3_consume
```
Alias for `fio_sha3_consume`. Feeds data into a SHAKE context.
#### `fio_shake_squeeze`
```c
void fio_shake_squeeze(fio_sha3_s *restrict h,
void *restrict out,
size_t outlen);
```
Squeezes `outlen` bytes from a SHAKE context. On the first call it applies padding and runs the first permutation; later calls continue squeezing and re-permute as needed.
**Parameters:**
- `h` - pointer to the SHAKE context.
- `out` - output buffer, must hold at least `outlen` bytes.
- `outlen` - number of bytes to squeeze.
#### `fio_shake128`
```c
void fio_shake128(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len);
```
One-shot SHAKE128.
**Parameters:**
- `out` - output buffer.
- `outlen` - number of bytes to produce.
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_shake256`
```c
void fio_shake256(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len);
```
One-shot SHAKE256.
**Parameters:**
- `out` - output buffer.
- `outlen` - number of bytes to produce.
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
### Examples
#### One-shot SHA3-256
```c
#define FIO_SHA3
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
uint8_t digest[32];
fio_sha3_256(digest, msg, strlen(msg));
for (size_t i = 0; i < sizeof(digest); ++i)
printf("%02x", digest[i]);
printf("\n");
return 0;
}
```
#### Streaming SHA3-512
```c
fio_sha3_s h = fio_sha3_512_init();
fio_sha3_consume(&h, "hello, ", 7);
fio_sha3_consume(&h, "world", 5);
uint8_t digest[64];
fio_sha3_finalize(&h, digest);
```
#### SHAKE256 extendable output
```c
const char *seed = "hello";
uint8_t out[128];
fio_sha3_s h = fio_shake256_init();
fio_shake_consume(&h, seed, strlen(seed));
fio_shake_squeeze(&h, out, sizeof(out));
```
**Note:** state is caller-owned; no heap allocation occurs. Keep SHA-3 and SHAKE contexts separate at finalization time (`fio_sha3_finalize` vs. `fio_shake_squeeze`).
------------------------------------------------------------
# AES-GCM
```c
#define FIO_AES
#include "fio-stl.h"
```
Authenticated encryption with associated data (AEAD) using AES-128-GCM and AES-256-GCM. The API is in-place and matches [`fio_chacha20_poly1305`](https://facil.io/0.8.x/chacha20-poly1305/), so the two cipher suites can be swapped with the same call shape.
Hardware acceleration is used when available (x86 AES-NI + PCLMULQDQ, ARM crypto extensions), with a portable fallback otherwise.
**Security note:** GCM nonces must never be reused with the same key. A 96-bit nonce runs out of uniqueness long before the key does; generate a fresh nonce for every message.
### API Functions
#### `fio_aes128_gcm_enc`
```c
SFUNC void fio_aes128_gcm_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Encrypts `data` in place with AES-128-GCM and writes a 16-byte authentication tag to `mac`.
**Parameters:**
- `mac` — output buffer; must have at least 16 writable bytes.
- `data` — plaintext buffer; encrypted in place. May be `NULL` if `len` is 0.
- `len` — plaintext length in bytes.
- `ad` — additional authenticated data; not encrypted but covered by the tag. May be `NULL`.
- `adlen` — length of `ad`.
- `key` — 16-byte AES-128 key.
- `nonce` — 12-byte nonce.
#### `fio_aes128_gcm_dec`
```c
SFUNC int fio_aes128_gcm_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Decrypts `data` in place and verifies the 16-byte tag in `mac`.
**Parameters:** same as `fio_aes128_gcm_enc`.
**Returns:** `0` on success, `-1` if authentication fails. On failure `data` is left unchanged.
#### `fio_aes256_gcm_enc`
```c
SFUNC void fio_aes256_gcm_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same as `fio_aes128_gcm_enc` but uses a 32-byte AES-256 key.
#### `fio_aes256_gcm_dec`
```c
SFUNC int fio_aes256_gcm_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same as `fio_aes128_gcm_dec` but uses a 32-byte AES-256 key.
### Example
```c
#define FIO_AES
#include "fio-stl.h"
int main(void) {
uint8_t key[32] = {0};
uint8_t nonce[12] = {0};
uint8_t msg[32] = "hello, gcm world!";
uint8_t tag[16];
fio_rand_bytes_secure(key, sizeof(key));
fio_rand_bytes_secure(nonce, sizeof(nonce));
fio_aes256_gcm_enc(tag, msg, sizeof(msg), NULL, 0, key, nonce);
/* msg is now ciphertext; tag authenticates it. */
if (fio_aes256_gcm_dec(tag, msg, sizeof(msg), NULL, 0, key, nonce)) {
printf("authentication failed\n");
return 1;
}
/* msg is restored plaintext. */
return 0;
}
```
------------------------------------------------------------
# Ed25519 & X25519
```c
#define FIO_ED25519
#include "fio-stl.h"
```
Elliptic-curve cryptography over Curve25519: Ed25519 for signatures and X25519 for key exchange. Both provide 128-bit security with compact 32-byte keys.
**Security note:** this implementation has not been independently audited. Use at your own risk, and prefer a tested cryptographic library when available.
### Ed25519 Digital Signatures
- Secret key: 32 bytes
- Public key: 32 bytes
- Signature: 64 bytes
#### `fio_ed25519_keypair`
```c
SFUNC void fio_ed25519_keypair(uint8_t secret_key[32], uint8_t public_key[32]);
```
Generates a new random Ed25519 key pair.
#### `fio_ed25519_public_key`
```c
SFUNC void fio_ed25519_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
```
Derives the public key from a secret key.
#### `fio_ed25519_sign`
```c
SFUNC int fio_ed25519_sign(uint8_t signature[64],
const void *message,
size_t len,
const uint8_t secret_key[32],
const uint8_t public_key[32]);
```
Signs a message. Returns `0` on success, `-1` on failure. The signature is deterministic.
#### `fio_ed25519_verify`
```c
SFUNC int fio_ed25519_verify(const uint8_t signature[64],
const void *message,
size_t len,
const uint8_t public_key[32]);
```
Verifies a signature. Returns `0` on success, `-1` on failure.
### X25519 Key Exchange
- Secret key: 32 bytes
- Public key: 32 bytes
- Shared secret: 32 bytes
#### `fio_x25519_keypair`
```c
SFUNC void fio_x25519_keypair(uint8_t secret_key[32], uint8_t public_key[32]);
```
Generates a new random X25519 key pair.
#### `fio_x25519_public_key`
```c
SFUNC void fio_x25519_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
```
Derives the public key from a secret key.
#### `fio_x25519_shared_secret`
```c
SFUNC int fio_x25519_shared_secret(uint8_t shared_secret[32],
const uint8_t secret_key[32],
const uint8_t their_public_key[32]);
```
Computes a shared secret. Returns `0` on success, `-1` if the result is the all-zero point.
**Note:** pass the shared secret through a KDF such as HKDF before using it as an encryption key.
### Key Conversion
#### `fio_ed25519_sk_to_x25519`
```c
SFUNC void fio_ed25519_sk_to_x25519(uint8_t x_secret_key[32],
const uint8_t ed_secret_key[32]);
```
Converts an Ed25519 secret key to an X25519 secret key.
#### `fio_ed25519_pk_to_x25519`
```c
SFUNC void fio_ed25519_pk_to_x25519(uint8_t x_public_key[32],
const uint8_t ed_public_key[32]);
```
Converts an Ed25519 public key to an X25519 public key.
### ECIES Public-Key Encryption
#### `FIO_X25519_CIPHERTEXT_LEN`
```c
#define FIO_X25519_CIPHERTEXT_LEN(message_len) ((message_len) + 48)
```
Plaintext plus 32-byte ephemeral public key and 16-byte MAC.
#### `FIO_X25519_PLAINTEXT_LEN`
```c
#define FIO_X25519_PLAINTEXT_LEN(ciphertext_len) \
((ciphertext_len) > 48 ? ((ciphertext_len)-48) : 0)
```
Reverses `FIO_X25519_CIPHERTEXT_LEN`. Returns `0` for invalid ciphertext.
#### `fio_x25519_encrypt`
```c
SFUNC int fio_x25519_encrypt(uint8_t *ciphertext,
const void *message,
size_t message_len,
fio_crypto_enc_fn encryption_function,
const uint8_t recipient_pk[32]);
```
Encrypts a message to an X25519 public key. Ciphertext format: `ephemeral_pk || mac || encrypted_data`.
**Parameters:**
- `ciphertext` — output buffer (`message_len + 48` bytes).
- `message` — plaintext.
- `message_len` — plaintext length.
- `encryption_function` — e.g., `fio_chacha20_poly1305_enc`.
- `recipient_pk` — recipient's 32-byte X25519 public key.
**Returns:** `0` on success, `-1` on failure.
#### `fio_x25519_decrypt`
```c
SFUNC int fio_x25519_decrypt(uint8_t *plaintext,
const uint8_t *ciphertext,
size_t ciphertext_len,
fio_crypto_dec_fn decryption_function,
const uint8_t recipient_sk[32]);
```
Decrypts a message using the recipient's X25519 secret key.
**Parameters:**
- `plaintext` — output buffer (`ciphertext_len - 48` bytes).
- `ciphertext` — ciphertext from `fio_x25519_encrypt`.
- `ciphertext_len` — total ciphertext length (must be at least 48).
- `decryption_function` — e.g., `fio_chacha20_poly1305_dec`.
- `recipient_sk` — recipient's 32-byte X25519 secret key.
**Returns:** `0` on success, `-1` on failure.
### Example
```c
#define FIO_ED25519
#include "fio-stl.h"
int main(void) {
uint8_t sk[32], pk[32], sig[64];
fio_ed25519_keypair(sk, pk);
const char *msg = "hello, ed25519";
fio_ed25519_sign(sig, msg, strlen(msg), sk, pk);
if (fio_ed25519_verify(sig, msg, strlen(msg), pk)) {
printf("bad signature\n");
return 1;
}
return 0;
}
```
------------------------------------------------------------
# P-256 (secp256r1)
```c
#define FIO_P256
#include "fio-stl.h"
```
P-256 is the NIST `secp256r1` curve used by TLS and certificate tooling. This module covers two jobs: ECDSA signatures over SHA-256 message hashes, and ECDH shared-secret creation.
**Security note:** this implementation has not been independently audited. Use it when the zero-dependency STL path matters; prefer a tested crypto library for long-lived keys, compliance, or high-value trust decisions.
## What It Provides
| Use | Function | Notes |
| --- | --- | --- |
| Sign | `fio_ecdsa_p256_sign` | Creates a DER-encoded ECDSA signature from a 32-byte SHA-256 hash. |
| Verify DER signature | `fio_ecdsa_p256_verify` | Accepts `SEQUENCE { r INTEGER, s INTEGER }` and a 65-byte uncompressed public key. |
| Verify raw signature | `fio_ecdsa_p256_verify_raw` | Accepts raw 32-byte `r`, `s`, `x`, and `y` values. |
| Key pair | `fio_p256_keypair` | Produces a 32-byte secret key and a 65-byte uncompressed public key. |
| Shared secret | `fio_p256_shared_secret` | Accepts compressed or uncompressed peer public keys and returns the x-coordinate. |
The curve parameters are the standard P-256 parameters from NIST FIPS 186-4:
- `p = 2^256 - 2^224 + 2^192 + 2^96 - 1`
- `n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551`
- curve equation: `y² = x³ - 3x + b (mod p)`
## ECDSA Signatures
### `fio_ecdsa_p256_sign`
```c
SFUNC int fio_ecdsa_p256_sign(uint8_t *sig,
size_t *sig_len,
size_t sig_capacity,
const uint8_t msg_hash[32],
const uint8_t secret_key[32]);
```
Signs a 32-byte SHA-256 message hash. The function writes a DER-encoded signature to `sig` and stores the actual length in `sig_len`.
- `sig_capacity` must be at least `72` bytes.
- `secret_key` is a 32-byte big-endian scalar and must be in the valid range `1..n-1`.
- The function generates a random ECDSA nonce internally and retries invalid nonces.
- Returns `0` on success, `-1` on invalid input or signing failure.
It signs the hash you give it; it does not hash the original message. No sneaky kitchen work here.
### `fio_ecdsa_p256_verify`
```c
SFUNC int fio_ecdsa_p256_verify(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
const uint8_t *pubkey,
size_t pubkey_len);
```
Verifies a DER-encoded ECDSA signature against a 32-byte SHA-256 hash.
- `sig` is a DER `SEQUENCE { r INTEGER, s INTEGER }`.
- `msg_hash` is exactly 32 bytes.
- `pubkey` must be an uncompressed P-256 public key: `0x04 || x || y`.
- `pubkey_len` must be `65`.
- Returns `0` for a valid signature, `-1` for invalid input or a bad signature.
### `fio_ecdsa_p256_verify_raw`
```c
SFUNC int fio_ecdsa_p256_verify_raw(const uint8_t r[32],
const uint8_t s[32],
const uint8_t msg_hash[32],
const uint8_t pubkey_x[32],
const uint8_t pubkey_y[32]);
```
Verifies a signature when `r` and `s` are already decoded. Public-key coordinates are 32-byte big-endian values.
The verifier checks that `r` and `s` are in range, checks the public point is on the P-256 curve, performs the ECDSA verification equation, and returns `0` only when the signature matches.
## ECDH Key Exchange
### `fio_p256_keypair`
```c
SFUNC int fio_p256_keypair(uint8_t secret_key[32], uint8_t public_key[65]);
```
Generates a P-256 key pair.
- `secret_key` receives a 32-byte scalar.
- `public_key` receives `0x04 || x || y` (65 bytes).
- Returns `0` on success, `-1` on bad arguments or random generation failure.
### `fio_p256_shared_secret`
```c
SFUNC int fio_p256_shared_secret(uint8_t shared_secret[32],
const uint8_t secret_key[32],
const uint8_t *their_public_key,
size_t their_public_key_len);
```
Computes a P-256 ECDH shared secret using your secret key and their public key.
- `secret_key` is a 32-byte scalar in the valid range `1..n-1`.
- `their_public_key` may be uncompressed (`65` bytes, `0x04 || x || y`) or compressed (`33` bytes, `0x02/0x03 || x`).
- The peer point is decompressed when needed and checked against the curve.
- `shared_secret` receives the 32-byte x-coordinate of the result.
- Returns `0` on success, `-1` on invalid input, invalid point, point at infinity, or all-zero result.
Run the shared secret through a KDF such as HKDF before using it as an encryption key. Raw ECDH output is an ingredient, not dinner.
## Example
```c
#define FIO_P256
#define FIO_SHA2
#include "fio-stl.h"
int main(void) {
uint8_t secret_key[32];
uint8_t public_key[65];
if (fio_p256_keypair(secret_key, public_key))
return 1;
const char *message = "hello, p-256";
fio_u256 hash = fio_sha256(message, strlen(message));
uint8_t sig[72];
size_t sig_len = 0;
if (fio_ecdsa_p256_sign(sig, &sig_len, sizeof(sig), hash.u8, secret_key))
return 1;
if (fio_ecdsa_p256_verify(sig, sig_len, hash.u8, public_key, 65))
return 1;
return 0;
}
```
------------------------------------------------------------
# P-384 (secp384r1)
```c
#define FIO_P384
#include "fio-stl.h"
```
P-384 is the NIST `secp384r1` curve. In this STL, it is a verifier: it checks ECDSA P-384 signatures, mainly for TLS 1.3 certificate chains and CA roots that use P-384.
There is no signing or key-generation API here. Just verification. Nice and focused.
**Security note:** this implementation has not been independently audited. It is meant for verification of public data; prefer a tested crypto library when certificate trust is central to your application.
## What It Provides
| Use | Function | Notes |
| --- | --- | --- |
| Verify DER signature | `fio_ecdsa_p384_verify` | Accepts `SEQUENCE { r INTEGER, s INTEGER }` and a 97-byte uncompressed public key. |
| Verify raw signature | `fio_ecdsa_p384_verify_raw` | Accepts raw 48-byte `r`, `s`, `x`, and `y` values. |
Curve parameters from NIST FIPS 186-4:
- `p = 2^384 - 2^128 - 2^96 + 2^32 - 1`
- `n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973`
- curve equation: `y² = x³ - 3x + b (mod p)`
- coordinate size: 48 bytes
## Verification API
### `fio_ecdsa_p384_verify`
```c
SFUNC int fio_ecdsa_p384_verify(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
const uint8_t *pubkey,
size_t pubkey_len);
```
Verifies a DER-encoded ECDSA P-384 signature over a 48-byte SHA-384 hash.
- `sig` is a DER `SEQUENCE { r INTEGER, s INTEGER }`.
- `sig_len` is the signature length in bytes.
- `msg_hash` is the SHA-384 hash of the message, exactly 48 bytes.
- `pubkey` must be an uncompressed public key: `0x04 || x || y`.
- `pubkey_len` must be `97`.
- Returns `0` for a valid signature, `-1` for invalid input or a bad signature.
The function validates the public-key wrapper, decodes `r` and `s` from DER, then calls `fio_ecdsa_p384_verify_raw`.
Compressed public keys are not accepted by this API.
### `fio_ecdsa_p384_verify_raw`
```c
SFUNC int fio_ecdsa_p384_verify_raw(const uint8_t r[48],
const uint8_t s[48],
const uint8_t msg_hash[48],
const uint8_t pubkey_x[48],
const uint8_t pubkey_y[48]);
```
Verifies an ECDSA P-384 signature when the signature and public key are already decoded.
All inputs are fixed-width big-endian values:
- `r` and `s`: 48-byte signature scalars.
- `msg_hash`: 48-byte SHA-384 hash.
- `pubkey_x` and `pubkey_y`: 48-byte public-key coordinates.
The verifier checks that `r` and `s` are in range, checks the public point is on the P-384 curve, computes the ECDSA verification equation, and returns `0` only when `r == R.x mod n`.
## Example
```c
#define FIO_P384
#include "fio-stl.h"
int verify_cert_sig(const uint8_t *sig,
size_t sig_len,
const uint8_t hash[48],
const uint8_t pubkey[97]) {
if (fio_ecdsa_p384_verify(sig, sig_len, hash, pubkey, 97))
return -1;
return 0;
}
```
## Implementation Notes
The implementation uses 6 × 64-bit limbs for field elements, Jacobian coordinates for point work, P-384-specific modular reduction, and a double-scalar multiplication path for ECDSA verification.
The scalar multiplication is for public verification data. Do not treat this header as a P-384 signing toolkit; there is no public signing API, and private-key use should go to an audited library.
------------------------------------------------------------
# ASN.1 DER
```c
#define FIO_DER
#include "fio-stl.h"
```
ASN.1 DER is the small binary grammar hiding inside X.509 certificates, keys, signatures, and TLS messages. This module parses and writes the pieces facil.io needs for that world.
The parser is non-allocating. Parsed elements point into the original DER buffer, so keep that buffer alive while you use the results. No buffer, no treasure map.
## Core Types
### `fio_der_element_s`
```c
typedef struct {
const uint8_t *data;
size_t len;
uint8_t tag;
uint8_t is_constructed;
uint8_t tag_class;
uint8_t tag_number;
} fio_der_element_s;
```
A parsed DER element. `data` points to the content bytes, after the tag and length fields. `len` is the content length. The remaining fields describe the tag byte.
### `fio_der_iterator_s`
```c
typedef struct {
const uint8_t *pos;
const uint8_t *end;
} fio_der_iterator_s;
```
Iterator state for walking a `SEQUENCE` or `SET` element.
### Tags and Classes
`fio_der_tag_e` defines the common universal tags, including `FIO_DER_INTEGER`, `FIO_DER_BIT_STRING`, `FIO_DER_OID`, `FIO_DER_SEQUENCE`, `FIO_DER_SET`, string types, `FIO_DER_UTC_TIME`, `FIO_DER_GENERALIZED_TIME`, and context wrappers `FIO_DER_CONTEXT_0` through `FIO_DER_CONTEXT_3`.
`fio_der_class_e` names the tag classes: universal, application, context-specific, and private.
## OID Values
OID constants live in the X.509 module (`FIO_X509_OID_*`, see the X.509
documentation). An OID value is a plain `fio_u128` where bytes 0-14 hold the
DER content bytes (zero-padded) and byte 15 holds the content length. The
length is folded in because `0x00` is a legal OID content byte (arc 0), so
padding alone could not distinguish `{..0B}` from `{..0B 00}` - different
OIDs. Oversized OIDs (content > 15 bytes) simply never match, which is safe:
AlgorithmIdentifier OIDs are attacker-controlled and RFC 5280 requires exact
match.
Build a value once per parsed element with `fio___der_oid_value` (internal),
then compare it against any number of constants with `fio___der_oid_eq`
(internal) - two u64-lane `==` comparisons, no memcmp, no call:
```c
fio_u128 oid = fio___der_oid_value(&elem); /* build ONCE per element */
if (fio___der_oid_eq(oid, FIO_X509_OID_COMMON_NAME)) { /* ... */ }
```
## Parsing
### `fio_der_parse`
```c
SFUNC const uint8_t *fio_der_parse(fio_der_element_s *elem,
const uint8_t *data,
size_t data_len);
```
Parses one DER element. On success, fills `elem` and returns a pointer to the next element. On error, returns `NULL`.
It rejects invalid DER lengths, truncated buffers, unsupported high-tag-number encodings, and indefinite lengths.
### `fio_der_element_total_len`
```c
FIO_IFUNC size_t fio_der_element_total_len(const fio_der_element_s *elem,
const uint8_t *data);
```
Returns the full encoded length of an element: tag + length + content. `data` must be the original pointer used to parse the element.
## Type Parsers
### `fio_der_parse_integer`
```c
SFUNC int fio_der_parse_integer(const fio_der_element_s *elem,
uint64_t *value);
```
Parses an ASN.1 `INTEGER`. For small integers, pass `value` and receive the 64-bit value. For large integers such as RSA moduli, pass `NULL` and use `elem->data` / `elem->len` directly. Leading zero bytes for positive integers are handled.
Returns `0` on success, `-1` on error.
### `fio_der_parse_bit_string`
```c
SFUNC int fio_der_parse_bit_string(const fio_der_element_s *elem,
const uint8_t **bits,
size_t *bit_len,
uint8_t *unused_bits);
```
Parses an ASN.1 `BIT STRING`. `bits` points into the element data after the unused-bit count byte. `bit_len` is the byte length of the bit payload. `unused_bits` is the number of unused bits in the last byte.
### `fio_der_parse_oid`
```c
SFUNC int fio_der_parse_oid(const fio_der_element_s *elem,
char *buf,
size_t buf_len);
```
Converts an ASN.1 `OBJECT IDENTIFIER` into dotted text such as `1.2.840.113549.1.1.11`.
Returns the number of characters written, excluding the NUL byte, or `-1` on error.
### `fio_der_parse_time`
```c
SFUNC int fio_der_parse_time(const fio_der_element_s *elem,
int64_t *unix_time);
```
Parses `UTCTime` or `GeneralizedTime` into a Unix timestamp. Times must be UTC (`Z`). Fractional seconds are skipped when present.
### `fio_der_parse_string`
```c
FIO_IFUNC const char *fio_der_parse_string(const fio_der_element_s *elem,
size_t *len);
```
Returns a pointer to the string bytes for supported ASN.1 string tags, with the byte length in `len`. This does not validate or transcode the text; it simply gives you the payload.
### `fio_der_parse_boolean`
```c
FIO_IFUNC int fio_der_parse_boolean(const fio_der_element_s *elem,
int *value);
```
Parses a DER boolean and writes `0` or non-zero to `value`.
## Walking Sequences and Sets
### `fio_der_iterator_init`
```c
FIO_IFUNC void fio_der_iterator_init(fio_der_iterator_s *it,
const fio_der_element_s *sequence);
```
Initializes an iterator over the content bytes of a parsed `SEQUENCE` or `SET`.
### `fio_der_iterator_next`
```c
SFUNC int fio_der_iterator_next(fio_der_iterator_s *it,
fio_der_element_s *elem);
```
Parses the next child element and advances the iterator. Returns `0` when an element was read, `-1` at end or on parse error.
### `fio_der_iterator_has_next`
```c
FIO_IFUNC int fio_der_iterator_has_next(const fio_der_iterator_s *it);
```
Returns `1` when the iterator still has bytes to parse, otherwise `0`.
## Tag Helpers
```c
FIO_IFUNC int fio_der_is_tag(const fio_der_element_s *elem, uint8_t tag);
FIO_IFUNC int fio_der_is_context_tag(const fio_der_element_s *elem,
uint8_t tag_num);
FIO_IFUNC uint8_t fio_der_tag_number(const fio_der_element_s *elem);
```
Use these to keep parser code readable:
- `fio_der_is_tag` checks a universal tag such as `FIO_DER_INTEGER`.
- `fio_der_is_context_tag` checks `[0]`, `[1]`, and friends.
- `fio_der_tag_number` returns the decoded tag number.
## Encoding
All encoder functions return the number of bytes written or needed. Pass `NULL` as `buf` to calculate the encoded length before writing.
### Basic Encoders
```c
SFUNC size_t fio_der_encode_length(uint8_t *buf, size_t len);
SFUNC size_t fio_der_encode_integer(uint8_t *buf,
const uint8_t *data,
size_t data_len);
SFUNC size_t fio_der_encode_integer_small(uint8_t *buf, uint64_t value);
SFUNC size_t fio_der_encode_null(uint8_t *buf);
SFUNC size_t fio_der_encode_boolean(uint8_t *buf, int value);
```
These write DER length fields, positive integers, `NULL`, and booleans. `fio_der_encode_integer` expects big-endian integer bytes and adds the leading zero byte when DER needs one to keep the integer positive.
OIDs are encoded with the internal `fio___der_encode_oid(buf, fio_u128 oid)`,
which TLV-wraps an OID value (see OID Values above) without any dot-string
parsing.
### String and Byte Encoders
```c
SFUNC size_t fio_der_encode_utf8_string(uint8_t *buf,
const char *str,
size_t str_len);
SFUNC size_t fio_der_encode_printable_string(uint8_t *buf,
const char *str,
size_t str_len);
SFUNC size_t fio_der_encode_bit_string(uint8_t *buf,
const uint8_t *bits,
size_t bit_len,
uint8_t unused_bits);
SFUNC size_t fio_der_encode_octet_string(uint8_t *buf,
const uint8_t *data,
size_t data_len);
```
These write the tag, DER length, and payload for common primitive values.
### Header Encoders
```c
SFUNC size_t fio_der_encode_sequence_header(uint8_t *buf, size_t content_len);
SFUNC size_t fio_der_encode_set_header(uint8_t *buf, size_t content_len);
SFUNC size_t fio_der_encode_context_header(uint8_t *buf,
uint8_t tag_num,
size_t content_len,
int constructed);
```
These write only the wrapper tag and length. Write the content bytes immediately after the returned header length.
### Time Encoders
```c
SFUNC size_t fio_der_encode_utc_time(uint8_t *buf, int64_t unix_time);
SFUNC size_t fio_der_encode_generalized_time(uint8_t *buf, int64_t unix_time);
```
These encode Unix timestamps as DER `UTCTime` or `GeneralizedTime`.
## Example
```c
#define FIO_DER
#include "fio-stl.h"
void scan_sequence(const uint8_t *der, size_t der_len) {
fio_der_element_s seq;
if (!fio_der_parse(&seq, der, der_len))
return;
if (!fio_der_is_tag(&seq, FIO_DER_SEQUENCE))
return;
fio_der_iterator_s it;
fio_der_iterator_init(&it, &seq);
fio_der_element_s elem;
while (fio_der_iterator_next(&it, &elem) == 0) {
if (fio_der_is_tag(&elem, FIO_DER_OID)) {
fio_u128 oid = fio___der_oid_value(&elem);
/* compare with FIO_X509_OID_* constants via fio___der_oid_eq, or */
char dot[128];
if (fio_der_parse_oid(&elem, dot, sizeof(dot)) > 0) {
/* dotted string available for diagnostics */
}
}
}
}
```
------------------------------------------------------------
# RSA Signatures
```c
#define FIO_RSA
#include "fio-stl.h"
```
RSA here means signature work for TLS and X.509: verify PKCS#1 v1.5 signatures, verify RSA-PSS signatures, and create RSA-PSS signatures for TLS 1.3 `CertificateVerify`.
Supported modulus sizes are 2048, 3072, and 4096 bits. Smaller keys do not get a party hat.
**Security note:** this implementation has not been independently audited. Prefer a tested crypto library for high-value private keys, compliance, HSM integration, or anything that keeps auditors awake.
## Constants and Hash IDs
```c
#define FIO_RSA_MAX_BITS 4096
#define FIO_RSA_MAX_BYTES (FIO_RSA_MAX_BITS / 8)
#define FIO_RSA_MAX_WORDS (FIO_RSA_MAX_BYTES / 8)
```
```c
typedef enum {
FIO_RSA_HASH_SHA256 = 0,
FIO_RSA_HASH_SHA384 = 1,
FIO_RSA_HASH_SHA512 = 2,
} fio_rsa_hash_e;
```
`hash_len` must match the selected hash: 32 bytes for SHA-256, 48 for SHA-384, and 64 for SHA-512.
## Key Types
### `fio_rsa_pubkey_s`
```c
typedef struct {
const uint8_t *n;
size_t n_len;
const uint8_t *e;
size_t e_len;
} fio_rsa_pubkey_s;
```
Public key for verification. `n` is the modulus and `e` is the public exponent, both as big-endian byte arrays. This matches the DER layout used in X.509 certificates.
### `fio_rsa_privkey_s`
```c
typedef struct {
const uint8_t *n; size_t n_len;
const uint8_t *d; size_t d_len;
const uint8_t *e; size_t e_len;
const uint8_t *p; size_t p_len;
const uint8_t *q; size_t q_len;
const uint8_t *dP; size_t dP_len;
const uint8_t *dQ; size_t dQ_len;
const uint8_t *qInv; size_t qInv_len;
} fio_rsa_privkey_s;
```
Private key for RSA-PSS signing. `n` and `d` are required. The public exponent `e` and CRT fields (`p`, `q`, `dP`, `dQ`, `qInv`) are optional.
When CRT parameters are available, signing uses CRT with message blinding. If only `n` and `d` are available, signing falls back to the non-CRT path.
## Verification
### `fio_rsa_verify_pkcs1`
```c
SFUNC int fio_rsa_verify_pkcs1(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_pubkey_s *key);
```
Verifies an RSA PKCS#1 v1.5 signature with `DigestInfo` encoding.
This covers the X.509 signature algorithms commonly named:
- `sha256WithRSAEncryption`
- `sha384WithRSAEncryption`
- `sha512WithRSAEncryption`
Parameters:
- `sig`: signature bytes; length must match the modulus length.
- `sig_len`: signature length in bytes.
- `msg_hash`: pre-computed message hash.
- `hash_len`: 32, 48, or 64 bytes.
- `hash_alg`: matching `FIO_RSA_HASH_*` value.
- `key`: RSA public key.
Returns `0` for a valid signature, `-1` on invalid input or a bad signature.
### `fio_rsa_verify_pss`
```c
SFUNC int fio_rsa_verify_pss(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_pubkey_s *key);
```
Verifies an RSA-PSS signature. This is the RSA signature style TLS 1.3 uses for `CertificateVerify`.
The implementation uses:
- MGF1 with the same hash function;
- salt length equal to the hash length;
- trailer byte `0xBC`.
Returns `0` for a valid signature, `-1` on invalid input or a bad signature.
## Signing
### `fio_rsa_sign_pss`
```c
SFUNC int fio_rsa_sign_pss(uint8_t *signature,
size_t *sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_privkey_s *key);
```
Creates an RSA-PSS signature using `RSASSA-PSS-SIGN` from RFC 8017, with the TLS 1.3 settings listed above.
- `signature` must have room for `key->n_len` bytes.
- `sig_len` receives the signature length, which equals `key->n_len` on success.
- `msg_hash` is already hashed; this function does not hash the message.
- `key` must include `n` and `d`; CRT fields are optional.
Returns `0` on success, `-1` on error.
PKCS#1 v1.5 signing is not exposed. TLS 1.3 does not want it for `CertificateVerify`, and this header agrees.
## Example: Verify an RSA-PSS Signature
```c
#define FIO_RSA
#define FIO_SHA2
#include "fio-stl.h"
int verify_pss(const uint8_t *sig,
size_t sig_len,
const uint8_t *modulus,
size_t modulus_len,
const uint8_t *exponent,
size_t exponent_len,
const void *message,
size_t message_len) {
fio_rsa_pubkey_s key = {
.n = modulus,
.n_len = modulus_len,
.e = exponent,
.e_len = exponent_len,
};
fio_u256 hash = fio_sha256(message, message_len);
return fio_rsa_verify_pss(sig, sig_len, hash.u8, 32,
FIO_RSA_HASH_SHA256, &key);
}
```
## Practical Notes
- The signature length must match the modulus length.
- The public exponent is usually `65537` (`0x010001`), but the API accepts DER-style big-endian bytes.
- The module works on pre-computed hashes. Pick the hash to match the certificate or protocol signature algorithm.
- For X.509 parsing, this module is normally used through [X.509](https://facil.io/0.8.x/http-1-x-parser/), which extracts the RSA key and dispatches verification.
------------------------------------------------------------
# ML-KEM-768
```c
#define FIO_MLKEM
#include "fio-stl.h"
```
ML-KEM-768 is the FIPS 203 module-lattice key encapsulation mechanism: a post-quantum way to agree on a shared secret. This header also exposes the TLS hybrid `X25519MLKEM768` shape, which combines ML-KEM-768 with X25519.
**Security note:** this implementation has not been independently audited. Use it carefully, and prefer audited crypto stacks for high-value deployments.
## ML-KEM-768 Sizes
```c
#define FIO_MLKEM768_PUBLICKEYBYTES 1184
#define FIO_MLKEM768_SECRETKEYBYTES 2400
#define FIO_MLKEM768_CIPHERTEXTBYTES 1088
#define FIO_MLKEM768_SSBYTES 32
#define FIO_MLKEM768_SYMBYTES 32
```
Parameters: `n = 256`, `k = 3`, `q = 3329`, `eta1 = 2`, `eta2 = 2`, `d_u = 10`, `d_v = 4`.
## ML-KEM-768 API
### `fio_mlkem768_keypair`
```c
SFUNC int fio_mlkem768_keypair(uint8_t pk[1184], uint8_t sk[2400]);
```
Generates a random ML-KEM-768 key pair using the system CSPRNG. Returns `0` on success, `-1` on failure.
### `fio_mlkem768_keypair_derand`
```c
SFUNC int fio_mlkem768_keypair_derand(uint8_t pk[1184],
uint8_t sk[2400],
const uint8_t coins[64]);
```
Generates a deterministic key pair. `coins` is exactly 64 bytes: `d || z`. This is mostly for test vectors and reproducible checks; production code should normally use `fio_mlkem768_keypair`.
### `fio_mlkem768_encaps`
```c
SFUNC int fio_mlkem768_encaps(uint8_t ct[1088],
uint8_t ss[32],
const uint8_t pk[1184]);
```
Creates a ciphertext and shared secret for the holder of `pk`. Randomness comes from the system CSPRNG. Send `ct`; keep `ss` as the shared secret input to your KDF or protocol.
### `fio_mlkem768_encaps_derand`
```c
SFUNC int fio_mlkem768_encaps_derand(uint8_t ct[1088],
uint8_t ss[32],
const uint8_t pk[1184],
const uint8_t coins[32]);
```
Deterministic encapsulation using 32 bytes of caller-provided randomness. Useful for known-answer tests. Boring in production, which is exactly the point.
### `fio_mlkem768_decaps`
```c
SFUNC int fio_mlkem768_decaps(uint8_t ss[32],
const uint8_t ct[1088],
const uint8_t sk[2400]);
```
Recovers the shared secret from a ciphertext and secret key.
ML-KEM uses implicit rejection: invalid ciphertexts produce a pseudorandom shared secret derived from the secret key and ciphertext instead of a loud failure. That prevents chosen-ciphertext games from turning your error path into an oracle.
Returns `0` for well-formed inputs.
## X25519MLKEM768 Hybrid
`X25519MLKEM768` is the TLS 1.3 hybrid key exchange shape from the ECDHE/ML-KEM draft. It needs `FIO_ED25519` too, because this STL implements X25519 in the Ed25519/Curve25519 header.
The name starts with X25519, but the bytes start with ML-KEM. Yes, naming is hard.
```c
#define FIO_X25519MLKEM768_PUBLICKEYBYTES (32 + 1184) /* 1216 */
#define FIO_X25519MLKEM768_SECRETKEYBYTES (32 + 2400) /* 2432 */
#define FIO_X25519MLKEM768_CIPHERTEXTBYTES (32 + 1088) /* 1120 */
#define FIO_X25519MLKEM768_SSBYTES 64
```
| Value | Layout |
| --- | --- |
| Public key | `ML-KEM-768_ek` (1184) `||` `X25519_pk` (32) |
| Secret key | `ML-KEM-768_dk` (2400) `||` `X25519_sk` (32) |
| Ciphertext | `ML-KEM-768_ct` (1088) `||` `X25519_ephemeral_pk` (32) |
| Shared secret | `ML-KEM-768_ss` (32) `||` `X25519_ss` (32) |
### `fio_x25519mlkem768_keypair`
```c
SFUNC int fio_x25519mlkem768_keypair(uint8_t pk[1216], uint8_t sk[2432]);
```
Generates both the ML-KEM-768 and X25519 key pairs with system randomness. Returns `0` on success.
### `fio_x25519mlkem768_encaps`
```c
SFUNC int fio_x25519mlkem768_encaps(uint8_t ct[1120],
uint8_t ss[64],
const uint8_t pk[1216]);
```
Performs ML-KEM encapsulation and X25519 ephemeral key exchange. The returned shared secret is the concatenation `ML-KEM-768_ss || X25519_ss`.
### `fio_x25519mlkem768_decaps`
```c
SFUNC int fio_x25519mlkem768_decaps(uint8_t ss[64],
const uint8_t ct[1120],
const uint8_t sk[2432]);
```
Decapsulates the ML-KEM part and computes the X25519 shared secret. Returns `0` on success, or `-1` if X25519 rejects the peer point. ML-KEM invalid ciphertexts still use implicit rejection.
## Example
```c
#define FIO_MLKEM
#include "fio-stl.h"
int roundtrip(void) {
uint8_t pk[FIO_MLKEM768_PUBLICKEYBYTES];
uint8_t sk[FIO_MLKEM768_SECRETKEYBYTES];
uint8_t ct[FIO_MLKEM768_CIPHERTEXTBYTES];
uint8_t sender_ss[FIO_MLKEM768_SSBYTES];
uint8_t receiver_ss[FIO_MLKEM768_SSBYTES];
if (fio_mlkem768_keypair(pk, sk))
return -1;
if (fio_mlkem768_encaps(ct, sender_ss, pk))
return -1;
if (fio_mlkem768_decaps(receiver_ss, ct, sk))
return -1;
return fio_memcmp(sender_ss, receiver_ss, sizeof(sender_ss));
}
```
## Implementation Notes
The implementation uses ML-KEM-768 parameters, NTT arithmetic, Montgomery and Barrett reduction, and optional NEON / AVX2 paths for vectorized polynomial work. Those SIMD paths are speed knobs, not different APIs.
------------------------------------------------------------
# X.509 Certificates
```c
#define FIO_X509
#include "fio-stl.h"
```
This module parses DER-encoded X.509v3 certificates for TLS 1.3 style certificate checks. It is a compact parser, verifier, trust-store checker, TLS Certificate-message splitter, and small self-signed certificate generator.
It is non-allocating while parsing: `fio_x509_cert_s` stores pointers into the original DER bytes. Keep those bytes alive. Certificates dislike disappearing floors.
**Scope note:** this is a minimal TLS-oriented X.509 implementation, not a full PKI policy engine. Not all X.509 features are supported.
## Supported Pieces
- RSA, ECDSA P-256, ECDSA P-384, and Ed25519 public keys.
- RSA PKCS#1 v1.5, RSA-PSS, ECDSA, and Ed25519 signature verification.
- Validity period checks.
- Hostname matching using SAN DNS names and CN fallback, with one-label wildcards.
- Basic Constraints and Key Usage.
- Certificate chain validation against an optional trust store.
- TLS 1.3 Certificate message parsing.
- Self-signed certificate generation for Ed25519 and P-256 keys.
## Main Types
### Key and Signature Enums
```c
typedef enum {
FIO_X509_KEY_UNKNOWN = 0,
FIO_X509_KEY_RSA = 1,
FIO_X509_KEY_ECDSA_P256 = 2,
FIO_X509_KEY_ECDSA_P384 = 3,
FIO_X509_KEY_ED25519 = 4,
} fio_x509_key_algo_e;
```
```c
typedef enum {
FIO_X509_SIGNATURE_UNKNOWN = 0,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA256 = 1,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA384 = 2,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA512 = 3,
FIO_X509_SIGNATURE_RSA_PSS_SHA256 = 4,
FIO_X509_SIGNATURE_RSA_PSS_SHA384 = 5,
FIO_X509_SIGNATURE_RSA_PSS_SHA512 = 6,
FIO_X509_SIGNATURE_ECDSA_SHA256 = 7,
FIO_X509_SIGNATURE_ECDSA_SHA384 = 8,
FIO_X509_SIGNATURE_ED25519 = 9,
} fio_x509_signature_algo_e;
```
### Key Usage and Errors
`fio_x509_key_usage_e` defines RFC 5280 key-usage bits such as `FIO_X509_KU_DIGITAL_SIGNATURE`, `FIO_X509_KU_KEY_ENCIPHERMENT`, and `FIO_X509_KU_KEY_CERT_SIGN`. ASN.1 bit strings use MSB-first ordering, so the constants look a little backwards until they save you from off-by-one sadness.
`fio_x509_error_e` gives chain validation results: `FIO_X509_OK`, parse failure, expired/not-yet-valid, signature failure, issuer mismatch, not-a-CA, no trust anchor, hostname mismatch, empty chain, and chain-too-long.
### `fio_x509_trust_store_s`
```c
typedef struct {
const uint8_t **roots;
const size_t *root_lens;
size_t root_count;
} fio_x509_trust_store_s;
```
A simple root store: arrays of DER certificate pointers and lengths.
### `fio_tls_cert_entry_s`
```c
typedef struct {
const uint8_t *cert;
size_t cert_len;
} fio_tls_cert_entry_s;
```
One certificate entry extracted from a TLS 1.3 `Certificate` handshake message.
### `fio_x509_cert_s`
```c
typedef struct fio_x509_cert_s fio_x509_cert_s;
```
The parsed certificate structure uses non-owning buffer views
(`fio_buf_info_s` / `fio_ubuf_info_s`) and packs to 256 bytes with no
interior padding:
- `verified`: non-zero when a TLS backend verified this certificate's chain
(peer inspection only; always zero after `fio_x509_parse`);
- `der`: a reference to the original DER bytes (not a copy);
- `fingerprint`: 32-byte SHA-256 of the DER bytes (see `fio_x509_fingerprint`);
- `version`, `serial`, and validity timestamps;
- raw subject and issuer DNs;
- subject CN;
- public key type and key data;
- signature algorithm and signature bytes;
- TBS certificate bytes used for verification;
- Basic Constraints (`is_ca`), Key Usage (`has_key_usage`, `key_usage`), and
SAN data (first DNS name, first IP address, and the raw SAN extension for
iterating the rest).
All view pointers reference the original DER buffer.
## Parsing and Single-Certificate Checks
### `fio_x509_parse`
```c
SFUNC int fio_x509_parse(fio_x509_cert_s *cert,
const uint8_t *der_data,
size_t der_len);
```
Parses one DER certificate. `cert` is zeroed first. Returns `0` on success, `-1` on parse error.
### `fio_x509_fingerprint`
```c
SFUNC void fio_x509_fingerprint(fio_x509_cert_s *cert);
```
Computes the SHA-256 of the certificate's DER bytes into
`cert->fingerprint` (32 raw bytes). Call after `fio_x509_parse`; hashing is
lazy so parsing never pays for it. Requires the SHA2 module.
### `fio_x509_verify_signature`
```c
SFUNC int fio_x509_verify_signature(const fio_x509_cert_s *cert,
const fio_x509_cert_s *issuer);
```
Verifies that `issuer` signed `cert`, using the issuer public key and the certificate signature algorithm. Returns `0` when the signature is valid, `-1` otherwise.
### `fio_x509_check_validity`
```c
FIO_IFUNC int fio_x509_check_validity(const fio_x509_cert_s *cert,
int64_t current_time);
```
Checks `not_before <= current_time <= not_after`. Returns `0` when the certificate is in its validity window.
### `fio_x509_match_hostname`
```c
SFUNC int fio_x509_match_hostname(const fio_x509_cert_s *cert,
const char *hostname,
size_t hostname_len);
```
Checks whether a certificate matches a hostname. SAN DNS entries are supported, with CN fallback. Wildcards are one-label wildcards such as `*.example.com`; they do not swallow `a.b.example.com`.
Returns `0` for a match, `-1` for no match.
Distinguished Names are compared as raw DER with the core
`FIO_BUF_INFO_IS_EQ` macro (e.g.,
`FIO_BUF_INFO_IS_EQ(cert.issuer, root.subject)`).
## Chain Validation
### `fio_x509_verify_chain`
```c
SFUNC int fio_x509_verify_chain(const uint8_t **certs,
const size_t *cert_lens,
size_t cert_count,
const char *hostname,
int64_t current_time,
fio_x509_trust_store_s *trust_store);
```
Validates a certificate chain for TLS-style use.
Expected order:
1. `certs[0]`: end-entity / server certificate.
2. `certs[1]`: intermediate that signed `certs[0]`.
3. `certs[n-1]`: closest-to-root certificate.
Validation parses all certificates, checks validity periods, optionally matches the hostname, verifies each signature with the next certificate, checks issuer/subject DN links, requires CA certificates where needed, and optionally checks the final certificate against `trust_store`.
Trust anchors are matched by (subject DN, public key): the anchor's key must
verify the final certificate's signature. This also covers self-signed
certificates — a same-named impostor with a different key is rejected.
Returns `FIO_X509_OK` (`0`) on success, or a `FIO_X509_ERR_*` code.
### `fio_x509_is_trusted`
```c
SFUNC int fio_x509_is_trusted(const fio_x509_cert_s *cert,
fio_x509_trust_store_s *trust_store);
```
Checks whether `cert` appears in the trust store by subject DN match. Returns `0` if trusted, `-1` if not found.
### `fio_x509_error_str`
```c
FIO_IFUNC const char *fio_x509_error_str(int error);
```
Returns a static string for a chain validation error code.
## TLS Certificate Messages
```c
#define FIO_TLS_CERT_PARSE_ERROR ((size_t)-1)
```
### `fio_tls_parse_certificate_message`
```c
SFUNC size_t fio_tls_parse_certificate_message(fio_tls_cert_entry_s *entries,
size_t max_entries,
const uint8_t *data,
size_t data_len);
```
Parses a TLS 1.3 `Certificate` message body, after the handshake header. It extracts certificate entries and skips per-certificate extensions.
Returns the number of certificates parsed, or `FIO_TLS_CERT_PARSE_ERROR` on malformed input.
## Self-Signed Certificate Generation
Generation supports Ed25519 and P-256 key pairs.
```c
typedef enum {
FIO_X509_KEYPAIR_ED25519 = 1,
FIO_X509_KEYPAIR_P256 = 2,
} fio_x509_keypair_type_e;
```
`fio_x509_keypair_s` stores the selected key type, secret key bytes, public key bytes, and their lengths. Use `fio_x509_keypair_clear` when done.
`fio_x509_cert_options_s` controls the subject CN, organization, organizational unit, country, validity window, SAN DNS names, CA flag, and key-usage bits.
### `fio_x509_keypair_ed25519`
```c
SFUNC int fio_x509_keypair_ed25519(fio_x509_keypair_s *keypair);
```
Generates an Ed25519 key pair for certificate signing. Requires the Ed25519 module to be included. Returns `0` on success.
### `fio_x509_keypair_p256`
```c
SFUNC int fio_x509_keypair_p256(fio_x509_keypair_s *keypair);
```
Generates a P-256 key pair for certificate signing. Requires the P-256 module to be included. Returns `0` on success.
### `fio_x509_self_signed_cert`
```c
SFUNC size_t fio_x509_self_signed_cert(uint8_t *buf,
size_t buf_len,
const fio_x509_keypair_s *keypair,
const fio_x509_cert_options_s *options);
```
Writes a DER-encoded self-signed X.509v3 certificate. Call with `buf == NULL` to get a worst-case buffer size, then call again with a real buffer.
Returns bytes written, or `0` on error.
### `fio_x509_keypair_clear`
```c
FIO_IFUNC void fio_x509_keypair_clear(fio_x509_keypair_s *keypair);
```
Securely clears key material and zeros the structure.
## Example: Parse and Check Hostname
```c
#define FIO_X509
#include "fio-stl.h"
int check_cert(const uint8_t *der, size_t der_len, int64_t now) {
fio_x509_cert_s cert;
if (fio_x509_parse(&cert, der, der_len))
return -1;
if (fio_x509_check_validity(&cert, now))
return -1;
if (fio_x509_match_hostname(&cert, "example.com", 11))
return -1;
return 0;
}
```
## Practical Notes
- Define the crypto modules you need before including the STL: RSA, P-256, P-384, Ed25519, SHA-2, and ASN.1 may all matter depending on certificate algorithms.
- `trust_store == NULL` skips root trust checking in `fio_x509_verify_chain`; useful for structural tests, not for real trust decisions.
- Hostname checks are DNS-name focused. IP address SAN handling is not a full replacement for platform certificate validation.
- Parsed strings and DNs are raw certificate data. Normalize policy elsewhere if your application needs browser-grade PKI behavior.
------------------------------------------------------------
# PEM Parser
```c
#define FIO_PEM
#include "fio-stl.h"
```
PEM wraps DER bytes in Base64 armor:
```text
-----BEGIN -----
-----END -----
```
This module extracts PEM blocks, decodes certificates, and parses private keys used by the TLS/X.509 helpers.
**Limit:** encrypted private keys are not supported. If the key asks for a password, this parser politely leaves the room.
## Supported Labels
- `CERTIFICATE`
- `PRIVATE KEY` (PKCS#8)
- `RSA PRIVATE KEY` (legacy PKCS#1)
- `EC PRIVATE KEY` (legacy SEC1)
Private-key parsing supports RSA, P-256, and Ed25519 where the required crypto modules are available.
## Types
### `fio_pem_key_type_e`
```c
typedef enum {
FIO_PEM_KEY_UNKNOWN = 0,
FIO_PEM_KEY_RSA = 1,
FIO_PEM_KEY_ECDSA_P256 = 2,
FIO_PEM_KEY_ED25519 = 3,
} fio_pem_key_type_e;
```
### `fio_pem_s`
```c
typedef struct {
const uint8_t *der;
size_t der_len;
const char *label;
size_t label_len;
} fio_pem_s;
```
A decoded PEM block. `der` points into the caller-provided DER output buffer. `label` points into the original PEM input.
### `fio_pem_private_key_s`
```c
typedef struct fio_pem_private_key_s fio_pem_private_key_s;
```
Parsed private-key material. The active union member depends on `type`:
- RSA: fixed-size byte arrays for `n`, `e`, `d`, optional `p`, `q`, `dP`, `dQ`, and `qInv`, each with a length.
- P-256: 32-byte private scalar, optional 65-byte uncompressed public key.
- Ed25519: 32-byte seed, optional 32-byte public key.
Call `fio_pem_private_key_clear` when done.
## API
### `fio_pem_parse`
```c
SFUNC size_t fio_pem_parse(fio_pem_s *out,
uint8_t *der_buf,
size_t der_buf_len,
const char *pem_data,
size_t pem_len);
```
Finds the next PEM block, decodes its Base64 body into `der_buf`, and fills `out`.
Returns the number of bytes consumed from `pem_data`, or `0` on error. To parse a file with multiple PEM blocks, advance by the returned count and call again.
### `fio_pem_parse_certificate`
```c
SFUNC int fio_pem_parse_certificate(fio_x509_cert_s *cert,
const char *pem_data,
size_t pem_len);
```
Finds a `CERTIFICATE` block, decodes it, and parses the DER certificate with the X.509 parser. Returns `0` on success, `-1` on error.
Requires the X.509 module for the certificate parse.
### `fio_pem_parse_private_key`
```c
SFUNC int fio_pem_parse_private_key(fio_pem_private_key_s *key,
const char *pem_data,
size_t pem_len);
```
Parses a PEM private key into `key`.
Supported forms:
| PEM label | Format | Supported keys |
| --- | --- | --- |
| `PRIVATE KEY` | PKCS#8 | RSA, P-256, Ed25519 |
| `RSA PRIVATE KEY` | PKCS#1 | RSA |
| `EC PRIVATE KEY` | SEC1 | P-256 |
Returns `0` on success, `-1` on unsupported format, missing dependency, encrypted key, or malformed input.
### `fio_pem_get_certificate_der`
```c
SFUNC size_t fio_pem_get_certificate_der(uint8_t *der_out,
size_t der_out_len,
const char *pem_data,
size_t pem_len);
```
Extracts a `CERTIFICATE` block as raw DER without parsing the X.509 structure. Returns the DER length written, or `0` on error.
### `fio_pem_private_key_clear`
```c
FIO_IFUNC void fio_pem_private_key_clear(fio_pem_private_key_s *key);
```
Securely zeros a parsed private-key structure.
## Example: Extract Certificate DER
```c
#define FIO_PEM
#include "fio-stl.h"
size_t load_der(uint8_t *der, size_t der_cap, const char *pem, size_t pem_len) {
return fio_pem_get_certificate_der(der, der_cap, pem, pem_len);
}
```
## Example: Parse Multiple Blocks
```c
#define FIO_PEM
#include "fio-stl.h"
void scan_pem(const char *pem, size_t len) {
uint8_t der[8192];
while (len) {
fio_pem_s block;
size_t used = fio_pem_parse(&block, der, sizeof(der), pem, len);
if (!used)
break;
/* block.label / block.label_len name the PEM type. */
/* block.der / block.der_len hold the decoded DER bytes. */
pem += used;
len -= used;
}
}
```
## Dependency Notes
- Generic `fio_pem_parse` needs only the PEM/base64 helpers.
- Certificate parsing needs X.509 support.
- Private key parsing uses ASN.1, and RSA/P-256/Ed25519 support depends on the matching crypto headers.
------------------------------------------------------------
# Argon2
```c
#define FIO_ARGON2
#include "fio-stl.h"
```
Argon2 is a memory-hard password hashing function from RFC 9106. It uses BLAKE2b underneath and lets you tune time, memory, and lane count so attackers have to pay real rent.
This implementation supports Argon2d, Argon2i, and Argon2id. It is single-threaded: `parallelism` affects the Argon2 lane layout, but lanes are processed sequentially.
When comparing Argon2 outputs, use `fio_ct_is_eq` or another constant-time comparison.
## Types
### `fio_argon2_type_e`
```c
typedef enum {
FIO_ARGON2D = 0,
FIO_ARGON2I = 1,
FIO_ARGON2ID = 2,
} fio_argon2_type_e;
```
- `FIO_ARGON2D`: data-dependent access, faster, weaker against side-channel observers.
- `FIO_ARGON2I`: data-independent access.
- `FIO_ARGON2ID`: hybrid mode, commonly recommended for password hashing.
### `fio_argon2_args_s`
```c
typedef struct {
fio_buf_info_s password;
fio_buf_info_s salt;
fio_buf_info_s secret;
fio_buf_info_s ad;
uint32_t t_cost;
uint32_t m_cost;
uint32_t parallelism;
uint32_t outlen;
fio_argon2_type_e type;
} fio_argon2_args_s;
```
Arguments:
| Field | Meaning |
| --- | --- |
| `password` | Password / message `P`. |
| `salt` | Salt / nonce `S`; use a unique random salt. |
| `secret` | Optional secret key `K`. |
| `ad` | Optional associated data `X`. |
| `t_cost` | Pass count, minimum `1`. |
| `m_cost` | Memory cost in KiB, minimum `8 * parallelism`. |
| `parallelism` | Lane count, minimum `1`; processed sequentially here. |
| `outlen` | Output length in bytes, minimum `4`, default `32`. |
| `type` | `FIO_ARGON2D`, `FIO_ARGON2I`, or `FIO_ARGON2ID`. |
## API
### `fio_argon2`
```c
SFUNC fio_u512 fio_argon2(fio_argon2_args_s args);
#define fio_argon2(...) fio_argon2((fio_argon2_args_s){__VA_ARGS__})
```
Computes an Argon2 hash and returns up to 64 bytes in `fio_u512`. Use the first `outlen` bytes; if `outlen` is `0`, the default is 32 bytes.
For outputs longer than 64 bytes, use `fio_argon2_hash`.
### `fio_argon2_hash`
```c
SFUNC int fio_argon2_hash(void *out, fio_argon2_args_s args);
#define fio_argon2_hash(out, ...) \
fio_argon2_hash(out, (fio_argon2_args_s){__VA_ARGS__})
```
Writes the Argon2 output into a caller-provided buffer. Supports arbitrary output lengths of at least 4 bytes.
Returns `0` on success, `-1` on error.
## Example
```c
#define FIO_ARGON2
#include "fio-stl.h"
int check_password(const char *password,
size_t password_len,
const void *salt,
size_t salt_len,
const uint8_t expected[32]) {
fio_u512 hash = fio_argon2(
.password = FIO_BUF_INFO2((void *)password, password_len),
.salt = FIO_BUF_INFO2((void *)salt, salt_len),
.t_cost = 3,
.m_cost = 65536, /* 64 MiB */
.parallelism = 4,
.outlen = 32,
.type = FIO_ARGON2ID);
return fio_ct_is_eq(hash.u8, expected, 32) ? 0 : -1;
}
```
## Practical Notes
- Store the salt and cost settings with the password hash. Future you will not remember them. Future you never does.
- Pick `FIO_ARGON2ID` for password hashing unless you have a specific reason not to.
- Raise `m_cost` and `t_cost` until the slow path is acceptable for your login flow.
- Do not compare hashes with `memcmp`; use constant-time comparison.
------------------------------------------------------------
# Lyra2
```c
#define FIO_LYRA2
#include "fio-stl.h"
```
Lyra2 is a memory-hard password hashing scheme built around a BLAKE2b sponge. It lets you tune time cost and memory matrix size so password checks are cheap for you and annoying for attackers. A fair trade.
This implementation matches the reference C setup with `nPARALLEL == 1`, `SPONGE == 0`, and `RHO == 1`. It is single-threaded.
When comparing Lyra2 outputs, use `fio_ct_is_eq` or another constant-time comparison.
## Arguments
```c
typedef struct {
fio_buf_info_s password;
fio_buf_info_s salt;
uint64_t t_cost;
uint64_t m_cost;
size_t outlen;
size_t n_cols;
} fio_lyra2_args_s;
```
| Field | Meaning |
| --- | --- |
| `password` | Password to hash. |
| `salt` | Salt for the hash; use a unique random salt. |
| `t_cost` | Time cost / number of rounds, minimum `1`. |
| `m_cost` | Memory cost / number of matrix rows, minimum `3`. |
| `outlen` | Output length in bytes; default `32` when `0`. |
| `n_cols` | Matrix column count; default `256` when `0`. |
## API
### `fio_lyra2`
```c
SFUNC fio_u512 fio_lyra2(fio_lyra2_args_s args);
#define fio_lyra2(...) fio_lyra2((fio_lyra2_args_s){__VA_ARGS__})
```
Computes a Lyra2 hash and returns up to 64 bytes in `fio_u512`. Use the first `outlen` bytes; when `outlen` is `0`, use the default 32 bytes.
For outputs longer than 64 bytes, use `fio_lyra2_hash`.
### `fio_lyra2_hash`
```c
SFUNC int fio_lyra2_hash(void *out, fio_lyra2_args_s args);
#define fio_lyra2_hash(out, ...) \
fio_lyra2_hash(out, (fio_lyra2_args_s){__VA_ARGS__})
```
Writes the Lyra2 output into a caller-provided buffer. Supports arbitrary output lengths.
Returns `0` on success, `-1` on error.
## Example
```c
#define FIO_LYRA2
#include "fio-stl.h"
int check_password(const char *password,
size_t password_len,
const void *salt,
size_t salt_len,
const uint8_t expected[32]) {
fio_u512 hash = fio_lyra2(
.password = FIO_BUF_INFO2((void *)password, password_len),
.salt = FIO_BUF_INFO2((void *)salt, salt_len),
.t_cost = 3,
.m_cost = 1024,
.outlen = 32);
return fio_ct_is_eq(hash.u8, expected, 32) ? 0 : -1;
}
```
## Practical Notes
- Store the salt, `t_cost`, `m_cost`, `n_cols`, and `outlen` next to the hash.
- Increase `m_cost` first when you want more memory pressure.
- Use constant-time comparison for verification.
- If you need the standardized RFC 9106 family, see [Argon2](https://facil.io/0.8.x/argon2/). Lyra2 is a different memory-hard tool.
------------------------------------------------------------
# OTP (TOTP)
```c
#define FIO_OTP
#include "fio-stl.h"
```
Time-based One-Time Password helper. Implements the TOTP algorithm over HMAC-SHA-1, compatible with common authenticator apps. It can generate keys, encode them as Base32, and compute codes for the current time or a fixed timestamp.
### Types
#### `fio_otp_settings_s`
```c
typedef struct {
/** The time interval for TOTP rotation. */
size_t interval; /* 30 == Google OTP */
/** The number of digits in the OTP. */
size_t digits; /* 6 == Google OTP */
/** The time offset (in `interval` units) from the current time. */
int64_t offset; /* 0 == Google OTP */
/** Set to true if the secret / key is in Hex instead of Base32. */
uint8_t is_hex;
/** Set to true if the secret / key is raw bit data (no encoding). */
uint8_t is_raw;
} fio_otp_settings_s;
```
Configuration for OTP generation. Fields omitted in the named-argument macro call default to zero; the implementation then substitutes the Google-OTP defaults (30-second interval, 6 digits, zero offset).
### API Functions
#### `fio_otp_generate_key`
```c
FIO_IFUNC fio_u128 fio_otp_generate_key(void);
```
Generates a cryptographically secure random 128-bit key.
**Returns:** a 16-byte random key suitable for TOTP secrets.
#### `fio_otp_print_key`
```c
FIO_IFUNC size_t fio_otp_print_key(char *dest, uint8_t *key, size_t len);
```
Encodes a key as a Base32 string for sharing with authenticator apps.
**Parameters:**
- `dest` — output buffer. Must be at least `len * 2 + 1` bytes.
- `key` — key bytes; if `NULL`, a new random key is generated and encoded.
- `len` — key length in bytes.
**Returns:** length of the encoded string written to `dest`.
#### `fio_otp`
```c
SFUNC uint32_t fio_otp(fio_buf_info_s secret, fio_otp_settings_s settings);
/* Named arguments using macro. */
#define fio_otp(secret, ...) fio_otp(secret, (fio_otp_settings_s){__VA_ARGS__})
```
Computes a TOTP for the current wall-clock time.
**Parameters:**
- `secret` — shared secret as a `fio_buf_info_s`.
**Named Arguments:**
| Argument | Type | Description |
|---|---|---|
| `interval` | `size_t` | rotation interval in seconds; defaults to 30 |
| `digits` | `size_t` | number of output digits; defaults to 6 |
| `offset` | `int64_t` | interval offset from now; defaults to 0 |
| `is_hex` | `uint8_t` | set if `secret` is Hex encoded |
| `is_raw` | `uint8_t` | set if `secret` is raw bytes |
**Returns:** the OTP value.
#### `fio_otp_at`
```c
SFUNC uint32_t fio_otp_at(fio_buf_info_s secret,
uint64_t unix_time,
fio_otp_settings_s settings);
/* Named arguments using macro. */
#define fio_otp_at(secret, unix_time, ...) \
fio_otp_at(secret, unix_time, (fio_otp_settings_s){__VA_ARGS__})
```
Computes a TOTP for a specific Unix timestamp. Useful for tests and RFC vectors.
**Parameters:**
- `secret` — shared secret as a `fio_buf_info_s`.
- `unix_time` — timestamp to compute the OTP for.
**Named Arguments:** same as `fio_otp`.
**Returns:** the OTP value.
### Example
```c
#define FIO_OTP
#include "fio-stl.h"
int main(void) {
/* Generate and print a new Base32 secret. */
char key_str[64];
fio_otp_print_key(key_str, NULL, 0);
printf("secret: %s\n", key_str);
/* Compute the current 6-digit code. */
uint32_t code = fio_otp(FIO_BUF_INFO1(key_str));
printf("code: %06u\n", code);
return 0;
}
```
------------------------------------------------------------
# Secrets
```c
#define FIO_SECRET
#include "fio-stl.h"
```
A small program-wide secret helper. It reads an optional `SECRET` environment variable at startup, hashes it with SHA-512, and stores the result masked in memory so the raw secret does not show up in core dumps. If no secret is provided, a random one is generated.
This is **not** a key-management vault — it is a convenience for deriving a stable program-wide token from something outside the source tree.
### Environment Variables
At startup (via `FIO_CONSTRUCTOR`), the module checks:
- `SECRET` — the secret value, plain text or Hex encoded.
- `SECRET_LENGTH` — optional byte length; if omitted, `strlen` is used.
If `SECRET` is unset or empty, a random 512-bit secret is generated.
### API Functions
#### `fio_secret_is_random`
```c
SFUNC bool fio_secret_is_random(void);
```
Returns `true` if the global secret was randomly generated (rather than loaded from the environment).
#### `fio_secret`
```c
SFUNC fio_u512 fio_secret(void);
```
Returns the global secret as a masked SHA-512 hash. The value is unmasked on return; keep the result on the stack and erase it promptly.
**Returns:** the SHA-512 hash of the program secret, unmasked.
#### `fio_secret_set`
```c
SFUNC void fio_secret_set(char *str, size_t len, bool is_random);
```
Sets the global secret from `str` and stores its masked SHA-512 hash.
**Parameters:**
- `str` — secret bytes; may be plain text or Hex encoded. Whitespace is ignored during Hex decoding.
- `len` — length of `str` in bytes.
- `is_random` — set `true` if the secret is randomly generated.
If `str` is `NULL` or `len` is `0`, a random secret is generated and `is_random` is forced to `true`.
#### `fio_secret_set_at`
```c
SFUNC void fio_secret_set_at(fio_u512 *secret, char *str, size_t len);
```
Hashes `str` with SHA-512 and stores the masked result in `secret`.
**Parameters:**
- `secret` — destination hash. If `NULL`, the function returns silently.
- `str` — secret bytes; Hex decoding and whitespace handling apply as in `fio_secret_set`.
- `len` — length of `str`.
If `str` is `NULL` or `len` is `0`, random bytes are hashed instead.
#### `fio_secret_at`
```c
SFUNC fio_u512 fio_secret_at(fio_u512 *secret);
```
Unmasks a secret hash stored by `fio_secret_set_at`.
**Parameters:**
- `secret` — pointer to a masked `fio_u512`.
**Returns:** the unmasked SHA-512 hash.
### Example
```c
#define FIO_SECRET
#include "fio-stl.h"
int main(void) {
/* Set a custom secret and read it back. */
fio_secret_set("hunter2", 7, 0);
fio_u512 s = fio_secret();
/* s now holds SHA-512("hunter2"), masked while in global storage. */
fio_secure_zero(&s, sizeof(s));
return 0;
}
```
------------------------------------------------------------
# Brotli
```c
#define FIO_BROTLI
#include "fio-stl.h"
```
Brotli support for RFC 7932 data. The decoder handles the real format: Huffman tables, context modes, static dictionary, transforms, and the usual compressed-data acrobatics. The encoder provides practical quality levels 1–6.
## API
### `fio_brotli_decompress`
```c
SFUNC size_t fio_brotli_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses Brotli data.
Return values:
| Condition | Return value |
| --- | --- |
| Success | Decompressed byte count, `<= out_len`. |
| Output too small | Required buffer size, `> out_len`. |
| Corrupt input | `0`. |
| Size query | Required size, or `0` if corrupt / not knowable by scan. |
Pass `out == NULL` or `out_len == 0` to query the decompressed size. The query path scans meta-block lengths without allocating.
### `fio_brotli_decompress_bound`
```c
FIO_IFUNC size_t fio_brotli_decompress_bound(size_t in_len);
```
Returns a conservative decompression bound. The practical formula is `in_len * 1032 + 1024`, capped at 4GB for very large inputs. Prefer the size-query mode of `fio_brotli_decompress` when possible.
### `fio_brotli_compress`
```c
SFUNC size_t fio_brotli_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int quality);
```
Compresses data using Brotli. `quality` is clamped to 1–6:
- `1–2`: fast greedy matching.
- `3–4`: lazy matching.
- `5–6`: hash chain, context modeling, and static dictionary use.
Use an output buffer sized with `fio_brotli_compress_bound`. Returns compressed length on success, `0` on error.
### `fio_brotli_compress_bound`
```c
FIO_IFUNC size_t fio_brotli_compress_bound(size_t in_len);
```
Returns a generous compression bound: `in_len + (in_len >> 2) + 1024`.
## Example
```c
#define FIO_BROTLI
#include "fio-stl.h"
int roundtrip(const void *src, size_t src_len) {
size_t cap = fio_brotli_compress_bound(src_len);
uint8_t *compressed = malloc(cap);
if (!compressed)
return -1;
size_t compressed_len = fio_brotli_compress(compressed, cap, src, src_len, 5);
if (!compressed_len) {
free(compressed);
return -1;
}
size_t plain_len = fio_brotli_decompress(NULL, 0, compressed, compressed_len);
uint8_t *plain = malloc(plain_len);
if (!plain) {
free(compressed);
return -1;
}
size_t actual = fio_brotli_decompress(plain, plain_len, compressed, compressed_len);
free(plain);
free(compressed);
return actual == src_len ? 0 : -1;
}
```
## Implementation Notes
The decompressor uses a 64-bit LSB-first bit reader, two-level packed Huffman tables with an 8-bit first level, the RFC 7932 static dictionary, 121 transforms, and context-dependent literal decoding.
The compressor emits Brotli streams with bounded memory use and quality-dependent matching. For tiny input, simpler paths may win because being clever has overhead too.
------------------------------------------------------------
# DEFLATE / Gzip
```c
#define FIO_DEFLATE
#include "fio-stl.h"
```
Raw DEFLATE compression/decompression (RFC 1951), gzip wrappers (RFC 1952), and a streaming API for WebSocket `permessage-deflate` (RFC 7692).
This module is built around a 64-bit bit buffer, packed Huffman tables, a 32KB sliding window, and word-at-a-time match copies. Translation: it tries hard not to be slow.
## Raw DEFLATE
### `fio_deflate_decompress`
```c
SFUNC size_t fio_deflate_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses raw DEFLATE data with no zlib or gzip wrapper.
Return values:
| Condition | Return value |
| --- | --- |
| Success | Decompressed byte count, `<= out_len`. |
| Output too small | Required buffer size, `> out_len`. |
| Corrupt input | `0`. |
| Size query | Required size, or `0` if corrupt. |
Pass `out == NULL` or `out_len == 0` to run a decode pass that counts output bytes without writing them.
### `fio_deflate_decompress_bound`
```c
FIO_IFUNC size_t fio_deflate_decompress_bound(size_t in_len);
```
Returns a conservative decompression bound. For exact sizing, prefer the `fio_deflate_decompress(NULL, 0, ...)` query mode.
### `fio_deflate_compress`
```c
SFUNC size_t fio_deflate_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int level);
```
Compresses raw DEFLATE data.
Compression levels:
- `0`: store only.
- `1–3`: fast.
- `4–6`: normal.
- `7–9`: best compression.
Returns compressed length on success, `0` on error.
### `fio_deflate_compress_bound`
```c
FIO_IFUNC size_t fio_deflate_compress_bound(size_t in_len);
```
Returns an upper bound for raw DEFLATE output size.
## Gzip Wrappers
### `fio_gzip_compress`
```c
SFUNC size_t fio_gzip_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int level);
```
Compresses with a gzip wrapper for uses such as HTTP `Content-Encoding: gzip`. The output includes the gzip header, DEFLATE payload, CRC32, and original size trailer.
Returns total output length, or `0` on error.
### `fio_gzip_decompress`
```c
SFUNC size_t fio_gzip_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses gzip data, including wrapper validation and CRC/size checks when data is actually decoded.
Return modes mirror `fio_deflate_decompress`: byte count, required size, or `0` on invalid input.
## Streaming API
### `fio_deflate_s`
```c
typedef struct fio_deflate_s fio_deflate_s;
```
Opaque streaming compression/decompression state (~32 bytes without
takeover). The streaming API supports two modes:
- **No-takeover (default, `fio_deflate_new`):** each flushed message is an
independent deflate stream. Compressor scratch (hash + token buffers)
comes from a contention-safe static slot pool (`FIO_STATIC_SAFE_ALLOC_DEF`)
checked out per call, so persistent per-context state is ~0. This is the
only mode the WebSocket layer negotiates (both `*_no_context_takeover`
flags are always forced).
- **Context takeover (`fio_deflate_new_takeover`):** matches may reference
the last 32KB of previous messages. The window (+ compressor hash) is
allocated inside the context's own block — the documented per-context cost
(~160KB compressor / ~32KB decompressor).
**Thread safety:** contexts are stateful and unsynchronized — use one
context per connection and serialize all `fio_deflate_push` calls per
context (one writer at a time). The static scratch pool is internally
synchronized; when every slot is momentarily busy, compression fails
gracefully (returns `0`).
**Overflow contract:** when the output buffer is too small, `fio_deflate_push`
(compression) and `fio_deflate_compress` return `0` — output is NEVER
silently truncated. Callers should treat `0` as "send uncompressed" or retry
with a correctly-sized buffer (`fio_deflate_compress_bound`, which is now
guaranteed sufficient at every level: the compressor falls back to stored
blocks whenever Huffman coding would expand, capping negative-gain output at
input + 5 bytes per 64KB block + a small header).
### `fio_deflate_new`
```c
SFUNC fio_deflate_s *fio_deflate_new(int level, int is_compress);
```
Creates a no-takeover streaming state. `is_compress != 0` creates a compressor; `0` creates a decompressor. Returns `NULL` on allocation failure. `level` is recorded for API compatibility; the streaming compressor always uses the fast greedy matcher (the one-shot `fio_deflate_compress` keeps levels 0-9).
### `fio_deflate_new_takeover`
```c
SFUNC fio_deflate_s *fio_deflate_new_takeover(int level, int is_compress);
```
Creates a streaming state with context takeover (cross-message history over the last 32KB). Allocates the window (+ compressor hash) inside the context's own block (~160KB compressor / ~32KB decompressor). `fio_deflate_destroy` resets the history (keeping the allocation).
### `fio_deflate_free`
```c
SFUNC void fio_deflate_free(fio_deflate_s *s);
```
Frees a streaming state.
### `fio_deflate_destroy`
```c
SFUNC void fio_deflate_destroy(fio_deflate_s *s);
```
Resets a streaming context. The input buffer is freed when it grew past 64KB, keeping persistent per-connection state bounded (no-takeover design).
### `fio_deflate_window_bits_set`
```c
SFUNC void fio_deflate_window_bits_set(fio_deflate_s *s, int bits);
```
Clamps compressor match distances to 2^`bits` (8..15, default 15). Used to honor `server_max_window_bits` from RFC 7692 negotiation. Decompression ignores it (any in-message distance up to 32KB is accepted).
### `fio_deflate_push`
```c
SFUNC size_t fio_deflate_push(fio_deflate_s *s,
void *out,
size_t out_len,
const void *in,
size_t in_len,
int flush);
```
Compresses or decompresses the next input chunk. Compression chunks input
into 32KB blocks (bounded scratch, no message-sized copies) and emits the
sync-flush trailer only on the message-final chunk.
- `flush == 0`: normal streaming (buffered up to 32KB, then auto-compressed).
- `flush == 1`: sync flush, useful at WebSocket frame boundaries.
For decompression, a return value greater than `out_len` means “retry with this much output space”; buffered input is preserved for that retry. Multi-block peer streams (e.g. zlib at any memLevel) inflate fully; the 9 completion bytes are appended internally.
## Example: Raw Roundtrip
```c
#define FIO_DEFLATE
#include "fio-stl.h"
int roundtrip(const void *src, size_t src_len) {
size_t cap = fio_deflate_compress_bound(src_len);
uint8_t *compressed = malloc(cap);
if (!compressed)
return -1;
size_t compressed_len = fio_deflate_compress(compressed, cap, src, src_len, 6);
if (!compressed_len) {
free(compressed);
return -1;
}
size_t plain_len = fio_deflate_decompress(NULL, 0, compressed, compressed_len);
uint8_t *plain = malloc(plain_len);
if (!plain) {
free(compressed);
return -1;
}
size_t actual = fio_deflate_decompress(plain, plain_len, compressed, compressed_len);
free(plain);
free(compressed);
return actual == src_len ? 0 : -1;
}
```
------------------------------------------------------------
# TLS 1.3 Standalone Library
```c
#define FIO_TLS13
#include "fio-stl.h"
```
Standalone TLS 1.3 crypto library — for IO-layer TLS see [`405 tls13.md`](https://facil.io/0.8.x/native-tls-1-3-io-backend/).
This header implements TLS 1.3 building blocks and standalone client/server state machines: key schedule, record protection, handshake message helpers, alerts, KeyUpdate, certificate-request helpers, and client/server encrypt/decrypt flows.
It is not the facil.io IO integration layer. If you want TLS plugged into the evented IO stack, use the `405` layer. If you want the raw TLS 1.3 machinery, this is the sharp box.
**Security note:** this implementation has not been independently audited. Prefer audited platform TLS for production trust decisions when practical.
## Requirements
The key schedule needs HKDF, which needs SHA-2. The full TLS paths also depend on the selected AEAD, key exchange, certificate, and signature modules. `FIO_CRYPTO` is the easy button when you want the whole crypto set.
Common dependencies:
- `FIO_HKDF` / `FIO_SHA2` for key derivation.
- `FIO_AES` for AES-GCM suites.
- `FIO_CHACHA` for ChaCha20-Poly1305.
- `FIO_ED25519` for X25519 and Ed25519.
- `FIO_P256`, `FIO_P384`, `FIO_RSA`, `FIO_X509`, `FIO_PEM` as certificate/key support requires.
## Constants
```c
#define FIO_TLS13_SHA256_HASH_LEN 32
#define FIO_TLS13_SHA384_HASH_LEN 48
#define FIO_TLS13_MAX_HASH_LEN 48
#define FIO_TLS13_AES128_KEY_LEN 16
#define FIO_TLS13_AES256_KEY_LEN 32
#define FIO_TLS13_CHACHA_KEY_LEN 32
#define FIO_TLS13_IV_LEN 12
```
The record layer uses 12-byte IVs and 16-byte AEAD tags. Maximum TLS plaintext is 16 KiB.
## Main Types
The public types include:
- `fio_tls13_content_type_e` — alert, handshake, application data, and legacy change-cipher-spec content types.
- `fio_tls13_handshake_type_e` — ClientHello, ServerHello, Certificate, Finished, KeyUpdate, and related handshake message IDs.
- `fio_tls13_cipher_suite_e` — `TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, and `TLS_CHACHA20_POLY1305_SHA256`.
- `fio_tls13_cipher_type_e` — the AEAD implementation selector for record keys.
- `fio_tls13_record_keys_s` — per-direction write key, IV, sequence number, key length, and cipher type.
- Parsed handshake structs such as `fio_tls13_server_hello_s`, `fio_tls13_encrypted_extensions_s`, `fio_tls13_certificate_s`, `fio_tls13_certificate_verify_s`, and `fio_tls13_certificate_request_s`.
- `fio_tls13_client_s` and `fio_tls13_server_s` — standalone client/server TLS state.
## Key Schedule
These functions implement RFC 8446 section 7 derivation rules:
| Function | Purpose |
| --- | --- |
| `fio_tls13_hkdf_expand_label` | TLS-specific `HKDF-Expand-Label`. |
| `fio_tls13_derive_secret` | `Derive-Secret(secret, label, transcript_hash)`. |
| `fio_tls13_derive_early_secret` | Derive Early Secret from PSK or empty PSK. |
| `fio_tls13_derive_handshake_secret` | Derive Handshake Secret from Early Secret and ECDHE. |
| `fio_tls13_derive_master_secret` | Derive Master Secret. |
| `fio_tls13_derive_traffic_keys` | Derive write key and IV from a traffic secret. |
| `fio_tls13_derive_finished_key` | Derive the Finished MAC key. |
| `fio_tls13_compute_finished` | Compute Finished verify data. |
| `fio_tls13_update_traffic_secret` | Advance application traffic secret for KeyUpdate. |
`use_sha384 != 0` selects SHA-384; otherwise SHA-256 is used.
## Record Layer
| Function | Purpose |
| --- | --- |
| `fio_tls13_build_nonce` | XOR sequence number into IV to build the AEAD nonce. |
| `fio_tls13_record_keys_init` | Initialize `fio_tls13_record_keys_s`. |
| `fio_tls13_record_keys_clear` | Securely clear record keys. |
| `fio_tls13_record_encrypt` | Encrypt one TLS record and advance sequence number. |
| `fio_tls13_record_decrypt` | Decrypt one TLS record, recover inner content type, and advance sequence number. |
Record encryption appends the inner content type before AEAD protection. Decryption strips padding and reports the recovered `fio_tls13_content_type_e`.
## Handshake Helpers
| Function | Purpose |
| --- | --- |
| `fio_tls13_write_handshake_header` | Write a TLS handshake header. |
| `fio_tls13_build_client_hello` | Build a ClientHello with X25519 key share, SNI, and cipher suites. |
| `fio_tls13_parse_server_hello` | Parse ServerHello. |
| `fio_tls13_parse_encrypted_extensions` | Parse EncryptedExtensions. |
| `fio_tls13_parse_certificate` | Parse TLS Certificate handshake body. |
| `fio_tls13_parse_certificate_verify` | Parse CertificateVerify. |
| `fio_tls13_build_finished` | Build Finished handshake message from verify data. |
| `fio_tls13_parse_finished` | Verify a Finished message against expected verify data. |
| `fio_tls13_parse_certificate_request` | Parse CertificateRequest. |
| `fio_tls13_build_certificate_request` | Build CertificateRequest. |
## Alerts and KeyUpdate
| Function | Purpose |
| --- | --- |
| `fio_tls13_build_alert` | Build a plaintext alert payload. |
| `fio_tls13_send_alert` | Build and encrypt an alert record. |
| `fio_tls13_send_alert_plaintext` | Build a plaintext alert record. |
| `fio_tls13_build_key_update` | Build a KeyUpdate handshake message. |
| `fio_tls13_parse_key_update` | Parse KeyUpdate and extract request flag. |
| `fio_tls13_process_key_update` | Process peer KeyUpdate and rotate read/write state as needed. |
| `fio_tls13_send_key_update_response` | Send the required response update when requested. |
## Standalone Client API
| Function | Purpose |
| --- | --- |
| `fio_tls13_client_init` | Initialize client state for a server name. |
| `fio_tls13_client_destroy` | Clear client state. |
| `fio_tls13_client_set_trust_store` | Attach an X.509 trust store pointer. |
| `fio_tls13_client_skip_verification` | Enable/disable certificate verification skipping. Handle with tongs. |
| `fio_tls13_client_get_cert_error` | Return the last certificate validation error. |
| `fio_tls13_client_is_cert_verified` | Report certificate and chain verification status. |
| `fio_tls13_client_start` | Build the initial ClientHello. |
| `fio_tls13_client_process` | Process incoming handshake/record bytes and optionally emit output. |
| `fio_tls13_client_encrypt` | Encrypt application data. |
| `fio_tls13_client_decrypt` | Decrypt application data. |
| `fio_tls13_client_is_connected` | Test for connected state. |
| `fio_tls13_client_is_error` | Test for error state. |
| `fio_tls13_client_alpn_set` | Configure ALPN protocols. |
| `fio_tls13_client_set_cert` | Configure client certificate/private key. |
| `fio_tls13_client_set_public_key` | Configure client public key. |
| `fio_tls13_client_cert_requested` | Report whether the server requested a client certificate. |
## Standalone Server API
| Function | Purpose |
| --- | --- |
| `fio_tls13_server_init` | Initialize server state. |
| `fio_tls13_server_destroy` | Clear server state. |
| `fio_tls13_server_set_cert_chain` | Configure the server certificate chain. |
| `fio_tls13_server_set_private_key` | Configure the private key. |
| `fio_tls13_server_process` | Process incoming handshake/record bytes and optionally emit output. |
| `fio_tls13_server_encrypt` | Encrypt application data. |
| `fio_tls13_server_decrypt` | Decrypt application data. |
| `fio_tls13_server_is_connected` | Test for connected state. |
| `fio_tls13_server_is_error` | Test for error state. |
| `fio_tls13_server_alpn_set` | Configure ALPN protocols. |
| `fio_tls13_server_require_client_cert` | Configure client-certificate request/require mode. |
| `fio_tls13_server_client_cert_received` | Report whether a client certificate arrived. |
| `fio_tls13_server_client_cert_verified` | Report whether the client certificate verified. |
## Example Shape
```c
#define FIO_TLS13
#define FIO_CRYPTO
#include "fio-stl.h"
void client_start(uint8_t *out, size_t cap) {
fio_tls13_client_s client;
fio_tls13_client_init(&client, "example.com");
int len = fio_tls13_client_start(&client, out, cap);
if (len < 0) {
/* handle error */
}
fio_tls13_client_destroy(&client);
}
```
## Practical Notes
- `190 tls13.md` documents the standalone TLS 1.3 implementation.
- [`405 tls13.md`](https://facil.io/0.8.x/native-tls-1-3-io-backend/) is the IO-layer integration point.
- Skipping certificate verification is for tests and controlled environments, not normal trust.
- Keep traffic secrets and record keys out of logs. They are not collectibles.
------------------------------------------------------------
# Dynamic Types — Module Group 200
The 200-range modules provide the core dynamic/generic types in the facil.io C STL: strings, arrays, maps, reference counting, and the FIOBJ soft-type system.
All five modules share the same design: **define a name macro, then include the header**. The preprocessor does the rest, generating a fully typed, prefixed API from static-inline functions and macros.
---
## The Pattern
```c
/* 1. Set the name (and any optional configuration macros). */
#define FIO_STR_NAME my_str
/* 2. Include the STL (single-header or multi-header). */
#include "fio-stl.h"
/* 3. Use the generated API — everything is prefixed with your name. */
my_str_s s = {0};
my_str_write(&s, "hello", 5);
my_str_destroy(&s);
```
Each inclusion is independent. You can define the same module multiple times in the same translation unit as long as you use a different name macro each time.
---
## Defining a Custom Type
| Name macro | What you get |
|---|---|
| `FIO_STR_NAME` | Binary-safe dynamic string type and API |
| `FIO_ARRAY_NAME` | Dynamic array for an element type you choose |
| `FIO_MAP_NAME` | Hash map or set (ordered or unordered) |
| `FIO_REF_NAME` | Reference-counted wrapper around any type |
| `FIO_FIOBJ` | Soft/dynamic object system (no extra name needed) |
Optional companion macros (e.g. `FIO_ARRAY_TYPE`, `FIO_MAP_KEY`, `FIO_MAP_VALUE`, `FIO_REF_TYPE`) let you configure element types, comparison functions, copy/destroy hooks, and more — consult the individual module docs for the full list.
---
## Modules
| Module | Name macro | Doc |
|---|---|---|
| Dynamic Strings | `FIO_STR_NAME` (or `FIO_STR_SMALL`) | [→ 201 string.md](./201 string.md) |
| Dynamic Arrays | `FIO_ARRAY_NAME` | [→ 202 array.md](./202 array.md) |
| Hash Maps / Sets | `FIO_MAP_NAME` | [→ 210 map.md](./210 map.md) |
| Reference Counting | `FIO_REF_NAME` | [→ 249 reference counter.md](./249 reference counter.md) |
| FIOBJ Soft Types | `FIO_FIOBJ` | [→ 250 fiobj.md](./250 fiobj.md) |
> **Note:** `210 map2.h` is a legacy file kept for backward compatibility and is not part of this active module group.
---
## Quick Example — Three Types, One File
```c
/* A small immutable-optimized string */
#define FIO_STR_SMALL tag_str
#include "fio-stl.h"
/* A dynamic array of those strings */
#define FIO_ARRAY_NAME tag_list
#define FIO_ARRAY_TYPE tag_str_s
#define FIO_ARRAY_TYPE_DESTROY(s) tag_str_destroy(&(s))
#include "fio-stl.h"
/* A map from string keys to integer values */
#define FIO_MAP_NAME word_count
#define FIO_MAP_KEY_KSTR /* use fio_keystr_s keys */
#define FIO_MAP_VALUE size_t
#include "fio-stl.h"
```
Each `#include` is processed independently. Define → include → use. That's it.
# Dynamic Strings — Module 201
Binary-safe dynamic strings, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_STR_NAME my_str /* required — sets the type and function prefix */
#include "fio-stl.h"
```
This generates `my_str_s` and a full API prefixed `my_str_*`.
Throughout this document the placeholder `STR` stands for whatever name you chose. All examples use `fio_str` as the name.
---
## Flavors
### Default — `FIO_STR_NAME`
The default struct stores capacity, length, and a buffer pointer (or embeds the string directly when it's short enough). On a 64-bit system the struct is 32 bytes, letting strings up to **30 bytes** live entirely inside the struct with no heap allocation.
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
```
Best for: strings that are written to multiple times, or strings in the 15–30 byte range that fit in the embedded slot.
### Small / immutable-optimized — `FIO_STR_SMALL`
```c
#define FIO_STR_SMALL key /* also sets FIO_STR_NAME = key */
#include "fio-stl.h"
```
Sets `FIO_STR_OPTIMIZE4IMMUTABILITY 1`, which drops the capacity field to shrink the struct to 16 bytes. Short strings (≤ 14 bytes on 64-bit) are still embedded. Editing long strings is slower because capacity must be recalculated on each write.
Best for: map keys, mostly-immutable strings, or any context where struct size matters more than write throughput.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_STR_NAME`:
| Macro | Default | Effect |
|---|---|---|
| `FIO_STR_OPTIMIZE_EMBEDDED` | `0` | Each unit adds `sizeof(char*)` to the struct, growing the embedded-string capacity. Max 4 (default) or 1 (`FIO_STR_SMALL`). |
| `FIO_STR_OPTIMIZE4IMMUTABILITY` | `0` | Drops the capacity field; set automatically by `FIO_STR_SMALL`. |
---
## Initialization Macros
| Macro | Notes |
|---|---|
| `FIO_STR_INIT` | Zero-initializes the struct. Use for stack or heap allocation. |
| `FIO_STR_INIT_EXISTING(buf, len, capa)` | Wraps an already-allocated buffer. **Not valid for `FIO_STR_SMALL`**. |
| `FIO_STR_INIT_STATIC(cstr)` | Wraps a string literal; `free` won't touch the buffer. **Not valid for `FIO_STR_SMALL`**. |
| `FIO_STR_INIT_STATIC2(buf, len)` | Same as above with explicit length. **Not valid for `FIO_STR_SMALL`**. |
```c
fio_str_s s = FIO_STR_INIT; /* stack, zero-initialized */
fio_str_s *p = malloc(sizeof(*p));
*p = (fio_str_s)FIO_STR_INIT; /* heap, zero-initialized */
fio_str_s ro = FIO_STR_INIT_STATIC("read-only literal");
```
---
## Lifecycle
### Heap allocation
```c
fio_str_s *STR_new(void); /* allocate + zero-initialize */
void STR_free(fio_str_s *s); /* destroy content + free struct */
```
### Stack / embedded destruction
```c
void STR_destroy(fio_str_s *s); /* free content, re-initialize struct */
```
### Initialization helpers
```c
/* Wrap a static/const buffer. Copies if it fits embedded, otherwise borrows. */
fio_str_info_s STR_init_const(fio_str_s *s, const char *str, size_t len);
/* Always copies src into s. */
fio_str_info_s STR_init_copy(fio_str_s *s, const char *str, size_t len);
/* Copies another STR object into dest. */
fio_str_info_s STR_init_copy2(fio_str_s *dest, fio_str_s *src);
```
### Detach / hand-off
```c
/* Remove string data from s, return a heap-allocated C string. s is reset. */
char *STR_detach(fio_str_s *s);
/* Free a pointer returned by STR_detach. */
void STR_dealloc(void *ptr);
```
`detach` always returns a dynamically allocated buffer (never embedded data). Free it with `STR_dealloc`, not plain `free`, unless you know the allocator matches.
---
## State Inspection
```c
fio_str_info_s STR_info(const fio_str_s *s); /* buf + len + capa */
fio_buf_info_s STR_buf (const fio_str_s *s); /* buf + len only */
char *STR_ptr (fio_str_s *s); /* char * to data */
size_t STR_len (fio_str_s *s); /* byte length */
size_t STR_capa(fio_str_s *s); /* allocated capacity */
```
`fio_str_info_s` is a plain struct: `{ size_t len; char *buf; size_t capa; }`. When `capa == 0` the buffer is read-only (frozen or static). Data is always NUL-terminated but may contain embedded NUL bytes — don't use `strlen` on it.
```c
void STR_freeze (fio_str_s *s); /* make read-only */
uint8_t STR_is_frozen (fio_str_s *s); /* 1 if frozen */
int STR_is_allocated(const fio_str_s *s); /* 1 if heap buffer in use */
int STR_is_eq (const fio_str_s *a,
const fio_str_s *b); /* binary equality */
uint64_t STR_hash (const fio_str_s *s,
uint64_t seed); /* Risky Hash */
```
---
## Memory Management
```c
/* Resize without reallocation (capped by current capacity).
Returns updated state. Shrinking may lose data beyond the new length. */
fio_str_info_s STR_resize(fio_str_s *s, size_t size);
/* Try to release unused capacity. Effect depends on allocator. */
void STR_compact(fio_str_s *s);
/* Reserve at least `amount` additional bytes beyond current length.
NOT available for FIO_STR_SMALL types. */
fio_str_info_s STR_reserve(fio_str_s *s, size_t amount);
```
`reserve` + manual write + `resize` is the fastest pattern for bulk construction:
```c
fio_str_s s = FIO_STR_INIT;
fio_str_info_s info = fio_str_reserve(&s, 128);
size_t written = my_encoder(info.buf + fio_str_len(&s), 128, src);
fio_str_resize(&s, fio_str_len(&s) + written);
```
---
## Write / Append
All write functions append to the **end** of the string and return the updated `fio_str_info_s`.
```c
fio_str_info_s STR_write(fio_str_s *s, const void *src, size_t src_len);
fio_str_info_s STR_concat(fio_str_s *dest, fio_str_s *src); /* alias: STR_join */
fio_str_info_s STR_replace(fio_str_s *s,
intptr_t start_pos, /* negative = from end */
size_t old_len, /* 0 = insert only */
const void *src,
size_t src_len); /* 0 = erase only */
```
### Multi-write macro
```c
FIO_STR_WRITE2(str_name, dest_ptr, ...fio_string_write_s items...);
```
Combines multiple writes in one call using `fio_string_write_s` descriptors:
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
fio_str_s s = FIO_STR_INIT;
FIO_STR_WRITE2(fio_str, &s,
FIO_STRING_WRITE_STR1("Hello "),
FIO_STRING_WRITE_STR1("World!"));
/* s now contains "Hello World!" */
fio_str_destroy(&s);
```
---
## Number Writing
```c
fio_str_info_s STR_write_i (fio_str_s *s, int64_t num); /* decimal */
fio_str_info_s STR_write_hex(fio_str_s *s, int64_t num); /* raw hex digits, no 0x prefix */
fio_str_info_s STR_write_bin(fio_str_s *s, int64_t num); /* binary repr */
```
---
## Formatted Writes
```c
fio_str_info_s STR_printf (fio_str_s *s, const char *fmt, ...);
fio_str_info_s STR_vprintf(fio_str_s *s, const char *fmt, va_list argv);
```
Both append to the end of `s`.
```c
fio_str_s msg = FIO_STR_INIT;
fio_str_printf(&msg, "items: %zu, rate: %.2f\n", count, rate);
puts(fio_str_ptr(&msg));
fio_str_destroy(&msg);
```
---
## UTF-8
```c
size_t STR_utf8_valid (fio_str_s *s); /* char count if valid, 0 if not */
size_t STR_utf8_len (fio_str_s *s); /* character count */
int STR_utf8_select(fio_str_s *s, intptr_t *pos, size_t *len); /* char→byte slice */
```
`utf8_select` converts a UTF-8 character position and length to byte offsets in-place. Sets `*pos = -1` on invalid UTF-8 before the selection point. Returns -1 on error, 0 on success.
---
## Escaping
### JSON / C escaping
```c
fio_str_info_s STR_write_escape (fio_str_s *s, const void *data, size_t len);
fio_str_info_s STR_write_unescape(fio_str_s *s, const void *escaped, size_t len);
```
### HTML escaping
```c
fio_str_info_s STR_write_html_escape (fio_str_s *s, const void *raw, size_t len);
fio_str_info_s STR_write_html_unescape(fio_str_s *s, const void *escaped, size_t len);
```
`write_html_unescape` handles numeric code-points and a minimal set of named entities — it is intentionally incomplete.
---
## Base64
```c
fio_str_info_s STR_write_base64enc(fio_str_s *s,
const void *data, size_t data_len,
uint8_t url_encoded); /* 1 for URL-safe variant */
fio_str_info_s STR_write_base64dec(fio_str_s *s,
const void *encoded, size_t encoded_len);
```
---
## File / FD Reading
```c
/* Append from open file descriptor (regular files only; fd stays open). */
fio_str_info_s STR_readfd(fio_str_s *s, int fd,
intptr_t start_at, intptr_t limit);
/* Open filename, append slice, close. limit==0 reads to EOF. */
fio_str_info_s STR_readfile(fio_str_s *s, const char *filename,
intptr_t start_at, intptr_t limit);
```
On failure `STR_readfile` returns a state with `buf == NULL`.
---
## Quick Example
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
void greet(void) {
fio_str_s s = FIO_STR_INIT;
fio_str_write(&s, "Hello", 5);
fio_str_write(&s, ", ", 2);
fio_str_write(&s, "world!", 6);
printf("%.*s\n", (int)fio_str_len(&s), fio_str_ptr(&s));
/* → Hello, world! */
fio_str_replace(&s, 0, 5, "Goodbye", 7);
printf("%.*s\n", (int)fio_str_len(&s), fio_str_ptr(&s));
/* → Goodbye, world! */
fio_str_destroy(&s);
}
```
### Heap-allocated with reference counting
```c
#define FIO_STR_NAME fio_str
#define FIO_REF_NAME fio_str
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_DESTROY(s) fio_str_destroy(&(s))
#include "fio-stl.h"
void example(void) {
fio_str_s *s = fio_str_new();
fio_str_write(s, "shared", 6);
fio_str_s *ref = fio_str_dup(s); /* increment ref count */
fio_str_free(ref); /* decrement; destroys string at zero */
fio_str_free(s);
}
```
---
## Embedded-string Sizes (64-bit reference)
| Variant | Struct size | Max embedded string |
|---|---|---|
| `FIO_STR_NAME` (default) | 32 bytes | 30 bytes |
| `FIO_STR_NAME` + `FIO_STR_OPTIMIZE_EMBEDDED 1` | 40 bytes | 38 bytes |
| `FIO_STR_SMALL` | 16 bytes | 14 bytes |
Two bytes are always reserved for the metadata byte and the NUL terminator.
---
See [→ 200 types-overview.md](./200 types-overview.md) for the shared define-include-use pattern and how to combine this type with arrays, maps, and reference counters.
# Dynamic Arrays — Module 202
Type-safe dynamic arrays, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_ARRAY_NAME ary /* required — sets the type and function prefix */
#define FIO_ARRAY_TYPE int /* optional — element type; default void * */
#include "fio-stl.h"
```
This generates `ary_s` and a full API prefixed `ary_*`.
Throughout this document `ARY` stands for whatever name you chose. `ARY_PTR` is shorthand for `FIO_ARRAY_PTR` (usually `ARY_s *`). All examples use `fio_ary` as the name.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_ARRAY_NAME`.
### Element type
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_TYPE` | `void *` | The element type stored in the array. |
| `FIO_ARRAY_TYPE_INVALID` | `NULL` / `{0}` | Sentinel value returned for out-of-bounds access. |
| `FIO_ARRAY_TYPE_INVALID_SIMPLE` | `1` if `{0}` | Set to `1` if the invalid element is all-zero bytes. |
| `FIO_ARRAY_TYPE_STR` | — | Shortcut: configures the array for `fio_keystr_s` string elements (sets type, copy, destroy, and compare automatically). |
### Element hooks
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_TYPE_COPY(dest, src)` | `dest = src` | Called when copying an element into the array. |
| `FIO_ARRAY_TYPE_DESTROY(obj)` | _(nothing)_ | Called when an element is removed or the array is destroyed. |
| `FIO_ARRAY_TYPE_CMP(a, b)` | `a == b` | Called by `find`, `remove2`, etc. |
| `FIO_ARRAY_TYPE_CONCAT_COPY(dest, src)` | same as `COPY` | Used during `concat`; override if concat needs different copy semantics. |
| `FIO_ARRAY_DESTROY_AFTER_COPY` | `1` if both hooks are non-trivial | When set, `DESTROY` is called on an element after it has been copied to an `old` pointer. |
### Memory / growth
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_PADDING` | `4` | Extra empty slots allocated beyond the requested capacity. |
| `FIO_ARRAY_EXPONENTIAL` | `0` | Set to `1` for exponential (doubling) growth. Linear growth with padding is the default. |
| `FIO_ARRAY_ENABLE_EMBEDDED` | `1` | Store small arrays directly inside the struct (no heap allocation). Values > 1 add extra struct fields to increase the embedded capacity. |
---
## Initialization
```c
ARY_s a = FIO_ARRAY_INIT; /* stack — zero-initialize */
ARY_s *p = ARY_new(); /* heap — allocate + zero-initialize */
```
`FIO_ARRAY_INIT` expands to `{0}`, so a zero-initializing `memset` also works.
---
## Lifecycle
```c
ARY_s *ARY_new(void); /* allocate + initialize on the heap */
void ARY_free(ARY_PTR ary); /* destroy elements + free the container */
void ARY_destroy(ARY_PTR ary); /* destroy elements, reset to empty (stack-safe) */
```
Use `ARY_destroy` for stack-allocated arrays. Use `ARY_new` / `ARY_free` for heap.
```c
#define FIO_ARRAY_NAME fio_ary
#define FIO_ARRAY_TYPE int
#include "fio-stl.h"
void example(void) {
fio_ary_s a = FIO_ARRAY_INIT;
fio_ary_push(&a, 1);
fio_ary_push(&a, 2);
fio_ary_push(&a, 3);
/* ... use ... */
fio_ary_destroy(&a);
}
```
---
## State Inspection
```c
uint32_t ARY_count(ARY_PTR ary); /* number of elements */
uint32_t ARY_capa (ARY_PTR ary); /* current capacity */
int ARY_is_embedded(ARY_PTR ary); /* 1 = embedded, 0 = heap alloc, -1 = error */
FIO_ARRAY_TYPE *ARY2ptr(ARY_PTR ary); /* raw C pointer to the first element */
```
`ARY_is_embedded` reports whether the array currently uses in-struct storage. `ARY2ptr` returns a pointer directly into the backing array. It is valid until the next mutating call. Useful for bulk reads or passing to functions that expect a C array.
---
## Memory Management
```c
/* Reserve capacity for at least `capa` additional elements.
Negative capa reserves space at the beginning of the array.
Returns the new capacity. */
uint32_t ARY_reserve(ARY_PTR ary, int64_t capa);
/* Attempt to shrink memory to fit the current element count. */
void ARY_compact(ARY_PTR ary);
```
Growth is linear by default (current count + padding). Exponential growth can be enabled with `FIO_ARRAY_EXPONENTIAL 1`. Always call `ARY_reserve` before large batch inserts to avoid repeated reallocation.
---
## Push / Pop (end of array)
```c
/* Append an element. Returns a pointer to the new element, or NULL on error. */
FIO_ARRAY_TYPE *ARY_push(ARY_PTR ary, FIO_ARRAY_TYPE data);
/* Remove the last element.
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 if the array is empty. */
int ARY_pop(ARY_PTR ary, FIO_ARRAY_TYPE *old);
```
```c
fio_ary_push(&a, 42);
int out;
if (!fio_ary_pop(&a, &out))
printf("popped: %d\n", out);
```
---
## Unshift / Shift (beginning of array)
```c
/* Prepend an element. Returns a pointer to the new element, or NULL on error.
May trigger memmove. */
FIO_ARRAY_TYPE *ARY_unshift(ARY_PTR ary, FIO_ARRAY_TYPE data);
/* Remove the first element.
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 if the array is empty. */
int ARY_shift(ARY_PTR ary, FIO_ARRAY_TYPE *old);
```
Both `push`/`pop` and heap-backed `unshift`/`shift` are O(1) amortized. The implementation maintains headroom at both ends of the heap buffer to keep prepend operations cheap; the tiny embedded path may move elements.
---
## Random Access
```c
/* Set the element at `index`. Grows the array if needed, filling gaps with
FIO_ARRAY_TYPE_INVALID.
Negative index counts from the end (-1 == last element).
If `old` is not NULL, the previous value is copied there before being
destroyed.
Returns a pointer to the set element, or NULL on error. */
FIO_ARRAY_TYPE *ARY_set(ARY_PTR ary, int64_t index, FIO_ARRAY_TYPE data,
FIO_ARRAY_TYPE *old);
/* Return the element at `index` (no copy performed).
Negative index counts from the end.
Returns FIO_ARRAY_TYPE_INVALID if out of bounds. */
FIO_ARRAY_TYPE ARY_get(ARY_PTR ary, int64_t index);
```
```c
fio_ary_set(&a, 5, 99, NULL); /* extend + set index 5 */
int v = fio_ary_get(&a, -1); /* last element */
int prev;
fio_ary_set(&a, 0, 7, &prev); /* replace index 0, capture old value */
```
---
## Concat
```c
/* Append all elements from `src` to `dest`.
`src` is not modified.
Returns `dest`, or NULL on allocation failure. */
ARY_PTR ARY_concat(ARY_PTR dest, ARY_PTR src);
```
Uses `FIO_ARRAY_TYPE_CONCAT_COPY` (defaults to `FIO_ARRAY_TYPE_COPY`) for each element, so managed types are properly duplicated.
---
## Search and Removal
```c
/* Find the first occurrence of `data` starting at `start_at`.
Negative `start_at` seeks backwards (-1 == last element).
Returns the index, or FIO_ARRAY_NOT_FOUND ((uint32_t)-1). */
uint32_t ARY_find(ARY_PTR ary, FIO_ARRAY_TYPE data, int64_t start_at);
/* Remove element at `index`. All subsequent elements shift left (memmove).
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 on error. O(n). */
int ARY_remove(ARY_PTR ary, int64_t index, FIO_ARRAY_TYPE *old);
/* Remove all occurrences of `data`. Elements are compacted in-place.
Returns the count of removed items. O(n). */
uint32_t ARY_remove2(ARY_PTR ary, FIO_ARRAY_TYPE data);
```
```c
#define FIO_ARRAY_NOT_FOUND ((uint32_t)-1)
uint32_t idx = fio_ary_find(&a, 42, 0);
if (idx != FIO_ARRAY_NOT_FOUND)
fio_ary_remove(&a, idx, NULL);
```
---
## Iteration
### Callback-based
```c
/** Iteration info passed to the callback. */
typedef struct ARY_each_s {
ARY_PTR const parent; /* the array being iterated */
uint64_t index; /* current element index */
int (*task)(struct ARY_each_s *info); /* the callback; may be updated mid-cycle */
void *udata; /* opaque user data */
FIO_ARRAY_TYPE value; /* the current element's value */
uint64_t padding; /* reserved */
} ARY_each_s;
/* Call `task` for each element starting at `start_at`.
Returning -1 from `task` stops the loop.
Returns the stop position (elements processed + start). */
uint32_t ARY_each(ARY_PTR ary,
int (*task)(ARY_each_s *info),
void *udata,
int64_t start_at);
```
### Pointer iteration
```c
/* Returns a pointer to the next element.
Pass NULL for `pos` to get the first element.
`first` tracks the base pointer across reallocations.
Returns NULL when the end is reached. */
FIO_ARRAY_TYPE *ARY_each_next(ARY_PTR ary,
FIO_ARRAY_TYPE **first,
FIO_ARRAY_TYPE *pos);
```
### Loop macro
```c
/* Iterates with a typed `pos` pointer.
`array_name` must be the literal macro name used when generating the type.
Avoid structural mutations inside the loop (push/pop/remove may invalidate pos). */
FIO_ARRAY_EACH(array_name, array_ptr, pos) { /* pos is a FIO_ARRAY_TYPE * */ }
```
```c
#define FIO_ARRAY_NAME fio_ary
#define FIO_ARRAY_TYPE int
#include "fio-stl.h"
void print_all(fio_ary_s *a) {
FIO_ARRAY_EACH(fio_ary, a, pos) {
printf("%d\n", *pos);
}
}
```
---
## Embedded Arrays
When `FIO_ARRAY_ENABLE_EMBEDDED` is set (the default), small arrays are stored directly inside the `ARY_s` struct, skipping heap allocation. For `void *` arrays on a 64-bit system, the struct has room for 2 pointers embedded.
Setting `FIO_ARRAY_ENABLE_EMBEDDED` to a value greater than 1 adds extra fields to the struct, increasing embedded capacity at the cost of a larger struct.
`ARY_is_embedded(ary)` returns `1` when the array is currently in embedded mode. `ARY_compact` will re-embed an array if its count fits within the embedded capacity.
---
## Practical Example — String Pointer Array
```c
#define FIO_ARRAY_NAME str_list
#define FIO_ARRAY_TYPE const char *
#include "fio-stl.h"
void example(void) {
str_list_s list = FIO_ARRAY_INIT;
str_list_push(&list, "apple");
str_list_push(&list, "banana");
str_list_push(&list, "cherry");
FIO_ARRAY_EACH(str_list, &list, pos) {
printf("%s\n", *pos);
}
str_list_destroy(&list);
}
```
---
## Practical Example — Struct Array with Custom Hooks
```c
typedef struct { int id; float val; } point_s;
#define FIO_ARRAY_NAME pt_ary
#define FIO_ARRAY_TYPE point_s
#define FIO_ARRAY_TYPE_CMP(a,b) ((a).id == (b).id)
#include "fio-stl.h"
void example(void) {
pt_ary_s a = FIO_ARRAY_INIT;
pt_ary_push(&a, (point_s){.id = 1, .val = 1.5f});
pt_ary_push(&a, (point_s){.id = 2, .val = 2.5f});
point_s key = {.id = 1};
uint32_t idx = pt_ary_find(&a, key, 0);
if (idx != FIO_ARRAY_NOT_FOUND)
printf("found at %u\n", idx);
pt_ary_destroy(&a);
}
```
---
## Performance Notes
- **Access / set**: O(1).
- **Iteration**: excellent cache locality.
- **push / pop**: O(1) amortized.
- **unshift / shift**: O(1) amortized for heap-backed arrays; the tiny embedded path may move elements.
- **find**: O(n) worst case.
- **remove / remove2**: O(n) — involve `memmove`.
- Growth is **linear** by default (count + `FIO_ARRAY_PADDING`). Use `FIO_ARRAY_EXPONENTIAL 1` or call `ARY_reserve` up-front to avoid repeated reallocations on large batch inserts.
- Capacity is limited to **32 bits** (max ~4 billion elements).
---
See [→ 200 types-overview.md](./200 types-overview.md) for the shared define-include-use pattern and how to combine arrays with strings, maps, and reference counters.
# Hash Maps and Sets — Module 210
Type-safe hash maps and sets, generated from a name macro. One header covers both unordered and ordered (FIFO/LRU) variants.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_MAP_NAME my_map /* required — sets the type and function prefix */
#define FIO_MAP_KEY uint64_t /* optional — key type; default is fio_str_info_s (bstr) */
#define FIO_MAP_VALUE void * /* optional — omit for a Set instead of a Map */
#include "fio-stl.h"
```
This generates `my_map_s` and a full API prefixed `my_map_*`.
Throughout this document `MAP` stands for whatever name you chose. All examples use `map` as the name.
---
## Sets vs. Maps
- **Set**: define only `FIO_MAP_NAME` (and optionally `FIO_MAP_KEY`). Each element is the key itself.
- **Hash Map**: also define `FIO_MAP_VALUE`. Elements are key→value pairs.
If `FIO_MAP_KEY` is left undefined (or `FIO_MAP_KEY_BSTR` is defined), keys default to binary strings via `fio_str_info_s` / `fio_bstr`.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_MAP_NAME`.
### Naming shortcuts
| Macro | Effect |
|---|---|
| `FIO_MAP_NAME name` | Required. Sets the type and function prefix. |
| `FIO_OMAP_NAME name` | Shortcut: defines `FIO_MAP_NAME` and forces `FIO_MAP_ORDERED 1`. |
| `FIO_UMAP_NAME name` | Shortcut: defines `FIO_MAP_NAME` and forces `FIO_MAP_ORDERED 0`. |
### Key configuration
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_KEY` | `fio_str_info_s` | External (API) key type. |
| `FIO_MAP_KEY_BSTR` | — | Shortcut: API keys are `fio_str_info_s`; internally stored as `fio_bstr` (heap-allocated, NUL-terminated). This is the default when `FIO_MAP_KEY` is not defined. |
| `FIO_MAP_KEY_KSTR` | — | Shortcut: API keys are `fio_str_info_s`; internally stored as `fio_keystr_s` with small-string optimisation (≤14 bytes inline on 64-bit). Better cache locality than `bstr`. |
| `FIO_MAP_KEY_INTERNAL` | `FIO_MAP_KEY` | Internal storage type (if different from the API type). |
| `FIO_MAP_KEY_FROM_INTERNAL(k)` | `k` | Converts internal storage → API type. |
| `FIO_MAP_KEY_COPY(dest, src)` | `(dest) = (src)` | Copies an API key into internal storage. |
| `FIO_MAP_KEY_CMP(a, b)` | `(a) == (b)` | Compares an internal key with an API key. |
| `FIO_MAP_KEY_DESTROY(key)` | _(nothing)_ | Destroys a key in internal storage (free resources). |
| `FIO_MAP_KEY_DISCARD(key)` | _(nothing)_ | Destroys an API key that was not inserted (e.g. if you pre-allocated or ref-counted it). |
### Value configuration
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_VALUE` | _(none — Set)_ | External (API) value type. Defining this makes the template a hash map. |
| `FIO_MAP_VALUE_BSTR` | — | Shortcut: binary-string values via `fio_bstr`. |
| `FIO_MAP_VALUE_INTERNAL` | `FIO_MAP_VALUE` | Internal storage type. |
| `FIO_MAP_VALUE_FROM_INTERNAL(v)` | `v` | Converts internal storage → API type. |
| `FIO_MAP_VALUE_COPY(dest, src)` | `(dest) = (src)` | Copies an API value into internal storage. |
| `FIO_MAP_VALUE_DESTROY(v)` | _(nothing)_ | Destroys a value in internal storage. |
| `FIO_MAP_VALUE_DISCARD(v)` | _(nothing)_ | Destroys an API value that was not stored. |
### Hashing
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_HASH_FN(key)` | _(not defined)_ | When defined, the map computes its own hash from the API key. Callers no longer pass a `hash` argument. |
| `FIO_MAP_RECALC_HASH` | `0` | Set to `1` to skip caching the hash per-node (saves 8 bytes/node). Requires `FIO_MAP_HASH_FN`. |
When `FIO_MAP_HASH_FN` is **not** defined, every `get`/`set`/`remove` call takes an explicit `uint64_t hash` argument — letting you salt the hash per-map for defence against hash-flooding.
### Ordering
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_ORDERED` | `0` | Set to `1` (or define without a value) for insertion-order (FIFO) iteration and `evict`. Adds 8 bytes per node. |
| `FIO_MAP_LRU` | _(not defined)_ | Implies `FIO_MAP_ORDERED 1` and keeps LRU-style iteration order. The value is ignored. Does **not** auto-evict; call `MAP_evict()` yourself to enforce a size limit. |
### Limits
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_ATTACK_LIMIT` | `16` | Max full hash collisions before the map assumes a hash-flooding attack and starts overwriting stale entries. |
| `FIO_MAP_CAPA_BITS_LIMIT` | `31` | Max exponent for internal capacity (2^31 ≈ 2 billion elements). Cannot exceed 31 without rewriting internal code. |
---
## Initialization
```c
MAP_s m = FIO_MAP_INIT; /* stack — zero-initialize */
MAP_s *p = MAP_new(); /* heap — allocate + zero-initialize */
```
`FIO_MAP_INIT` expands to `{0}`.
---
## Lifecycle
```c
MAP_s *MAP_new(void); /* allocate + initialize on the heap */
void MAP_free(MAP_PTR map); /* destroy elements + free the container */
void MAP_destroy(MAP_PTR map); /* destroy elements, reset to empty (stack-safe) */
```
Use `MAP_destroy` for stack-allocated maps. Use `MAP_new` / `MAP_free` for heap.
---
## State
```c
uint32_t MAP_count(MAP_PTR map); /* number of stored elements */
uint32_t MAP_capa (MAP_PTR map); /* theoretical capacity (always a power of 2) */
void MAP_reserve(MAP_PTR map, size_t capa); /* ensure at least capa slots exist */
void MAP_compact(MAP_PTR map); /* shrink allocation to fit current count */
```
---
## Get, Set, Remove
The exact signature depends on whether `FIO_MAP_HASH_FN` is defined and whether `FIO_MAP_VALUE` is defined. Below `[hash,]` means the `uint64_t hash` argument is present only when `FIO_MAP_HASH_FN` is **not** defined.
```c
/* Returns the stored value (or key for a Set). Zero-value if not found. */
MAP_GET_T MAP_get(MAP_PTR map, [uint64_t hash,] MAP_KEY key);
/* Insert or overwrite. Returns the resulting stored value/key.
For hash maps: `old` (optional) receives the previous value before it is destroyed. */
MAP_GET_T MAP_set(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val, MAP_VALUE_INTERNAL *old]);
/* Insert only if the key is absent. Returns current value/key. */
MAP_GET_T MAP_set_if_missing(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val]);
/* Remove. Returns 0 on success, -1 if not found.
`old` (optional): receives the removed value/key before it is destroyed. */
/* For hash maps: */
int MAP_remove(MAP_PTR map, [uint64_t hash,] MAP_KEY key,
MAP_VALUE_INTERNAL *old);
/* For sets: */
int MAP_remove(MAP_PTR map, [uint64_t hash,] MAP_KEY key,
MAP_KEY_INTERNAL *old);
/* Evict `n` elements. Order depends on FIO_MAP_LRU / FIO_MAP_ORDERED. */
void MAP_evict(MAP_PTR map, size_t n);
/* Remove all elements without freeing the internal buffer. */
void MAP_clear(MAP_PTR map);
```
---
## Low-Level Node Access
`MAP_get_ptr` and `MAP_set_ptr` return a pointer into internal storage. Use when you need to mutate a value in place or avoid a copy.
```c
/* Returns internal node pointer, or NULL if missing. */
MAP_node_s *MAP_get_ptr(MAP_PTR map, [uint64_t hash,] MAP_KEY key);
/* Core insert/overwrite. `overwrite=1` replaces an existing value; `overwrite=0` keeps it.
Returns internal node pointer, or NULL on error. */
MAP_node_s *MAP_set_ptr(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val,
MAP_VALUE_INTERNAL *old, int overwrite]);
```
Accessors for node pointers:
```c
MAP_KEY MAP_node2key (MAP_node_s *node); /* external key */
MAP_VALUE MAP_node2val (MAP_node_s *node); /* external value (or key for Sets) */
uint64_t MAP_node2hash (MAP_node_s *node); /* hash value */
MAP_KEY_INTERNAL *MAP_node2key_ptr(MAP_node_s *node); /* pointer to internal key */
MAP_VALUE_INTERNAL*MAP_node2val_ptr(MAP_node_s *node); /* pointer to internal value */
```
All return a zeroed/NULL result when `node` is NULL.
---
## Iteration
### `FIO_MAP_EACH` / `FIO_MAP_EACH_REVERSED` — for-loop macros (preferred)
```c
FIO_MAP_EACH(map_name, map_ptr, i) {
/* i.key — current key (API type) */
/* i.value — current value (API type; absent for Sets) */
/* i.hash — current hash (absent if FIO_MAP_RECALC_HASH is set) */
}
FIO_MAP_EACH_REVERSED(map_name, map_ptr, i) { /* same, backward */ }
```
`i` is declared inside the loop as a `MAP_iterator_s`.
**Example:**
```c
#define FIO_MAP_NAME wcount
#define FIO_MAP_KEY_KSTR /* fio_str_info_s API keys, fio_keystr_s storage */
#define FIO_MAP_VALUE size_t
#define FIO_MAP_HASH_FN(k) fio_risky_hash((k).buf, (k).len, (uintptr_t)wcount_destroy)
#include "fio-stl.h"
void print_counts(wcount_s *m) {
FIO_MAP_EACH(wcount, m, it)
printf("%.*s => %zu\n", (int)it.key.len, it.key.buf, it.value);
}
```
### Manual iterator API
```c
/* Returns the first iterator (NULL current_pos) or the next one. */
MAP_iterator_s MAP_get_next(MAP_PTR map, MAP_iterator_s *current_pos);
/* Reverse: returns the last (NULL) or the previous one. */
MAP_iterator_s MAP_get_prev(MAP_PTR map, MAP_iterator_s *current_pos);
/* Returns 1 if valid, 0 if exhausted or invalidated. */
int MAP_iterator_is_valid(MAP_iterator_s *iter);
/* Returns the internal node pointer for an iterator. */
MAP_node_s *MAP_iterator2node(MAP_PTR map, MAP_iterator_s *iter);
```
Modifying the map (insert, rehash) between iterator steps is safe but may invalidate or reorder iterators for unordered maps.
### Callback API
```c
/* Calls task(info) for each element starting at `start_at`.
Returning -1 from the callback stops the loop.
Returns the final position (items processed + start_at). */
uint32_t MAP_each(MAP_PTR map,
int (*task)(MAP_each_s *info),
void *udata,
ssize_t start_at);
```
The callback receives a `MAP_each_s` pointer with:
```c
typedef struct {
MAP_PTR const parent; /* the map being iterated */
uint64_t index; /* current element index */
int (*task)(MAP_each_s *); /* the callback (may be swapped mid-loop) */
void *udata; /* opaque user data */
MAP_VALUE value; /* current value (absent for Sets) */
MAP_KEY key; /* current key */
} MAP_each_s;
```
---
## Practical Examples
### String-key dictionary (default keys, bstr values)
```c
#define FIO_MAP_NAME dict
#define FIO_MAP_VALUE_BSTR /* string values via fio_bstr */
#include "fio-stl.h"
/* Wrap set/get to salt the hash with the map pointer for security. */
static inline fio_str_info_s dict_put(dict_s *m, fio_str_info_s k, fio_str_info_s v) {
return dict_set(m, fio_risky_hash(k.buf, k.len, (uint64_t)(uintptr_t)m), k, v, NULL);
}
static inline fio_str_info_s dict_fetch(dict_s *m, fio_str_info_s k) {
return dict_get(m, fio_risky_hash(k.buf, k.len, (uint64_t)(uintptr_t)m), k);
}
void example(void) {
dict_s d = FIO_MAP_INIT;
dict_put(&d, FIO_STR_INFO1("hello"), FIO_STR_INFO1("world"));
fio_str_info_s v = dict_fetch(&d, FIO_STR_INFO1("hello"));
printf("%.*s\n", (int)v.len, v.buf); /* "world" */
dict_destroy(&d);
}
```
### Automatic hashing (FIO_MAP_HASH_FN)
```c
#define FIO_MAP_NAME imap
#define FIO_MAP_KEY int
#define FIO_MAP_VALUE float
#define FIO_MAP_HASH_FN(k) fio_risky_num((uint64_t)(k), 0)
#include "fio-stl.h"
void example(void) {
imap_s m = FIO_MAP_INIT;
imap_set(&m, 42, 3.14f, NULL);
printf("%f\n", imap_get(&m, 42)); /* 3.14 */
imap_destroy(&m);
}
```
### LRU cache (manual eviction)
```c
#define FIO_MAP_NAME cache
#define FIO_MAP_KEY_KSTR /* efficient internal string storage */
#define FIO_MAP_VALUE void *
#define FIO_MAP_LRU /* enables LRU ordering; value is ignored */
#define FIO_MAP_HASH_FN(k) fio_risky_hash((k).buf, (k).len, 0)
#include "fio-stl.h"
```
`FIO_MAP_LRU` only controls ordering. Enforce the size limit yourself after inserting:
```c
static inline void cache_put(cache_s *c, fio_str_info_s k, void *v) {
cache_set(c, k, v, NULL);
if (cache_count(c) > (1ULL << 12))
cache_evict(c, 1);
}
```
---
## Notes
- **`210 map2.h`** is a legacy implementation. It is not documented here and should not be used in new code.
- Pointer tagging (`FIO_PTR_TAG`) and reference counting (`FIO_REF_NAME`) are supported, same as all 200-range modules.
- The map internally stores a compact 8-bit hash fingerprint alongside each node to speed up probing. Full hash is cached per-node unless `FIO_MAP_RECALC_HASH 1` is set.
- Against hash-flooding: after `FIO_MAP_ATTACK_LIMIT` full collisions the map falls back to overwriting stale entries. Always salt hashes with a per-map value (e.g., the map pointer) when keys come from untrusted input.
---
See also: [← 200 types-overview.md](./200 types-overview.md) · [201 string.md](./201 string.md) · [202 array.md](./202 array.md)
# Reference Counting Wrapper — Module 249
Heap-allocate any type with an atomic reference count attached, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_REF_NAME my_obj /* required */
#define FIO_REF_TYPE my_obj_s /* optional — defaults to FIO_REF_NAME_s */
#define FIO_REF_CONSTRUCTOR_ONLY /* optional — see below */
#include "fio-stl.h"
```
This generates a thin wrapper struct that sits **before** the `FIO_REF_TYPE` object in memory. The caller only ever sees a `FIO_REF_TYPE *` (or the tagged pointer type if `FIO_PTR_TAG_TYPE` is set). The reference count, optional metadata, and optional flex-array length are all hidden in the prefix.
Throughout this document `REF` stands for whatever name you chose (e.g. `my_obj`).
> **Requires:** the `FIO_ATOMIC` helpers must be available (included automatically when using `fio-stl.h` as a single header).
---
## Configuration Macros
Set these **before** `#include`, after `FIO_REF_NAME`.
### Wrapped type
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_TYPE` | `FIO_REF_NAME_s` | The C type to wrap and reference-count. |
### Constructor / destructor naming
| Macro | Effect |
|---|---|
| _(not defined)_ | Generates `REF_new2`, `REF_dup2`, `REF_free2` — the `2` suffix leaves room for a separately-defined `REF_new` / `REF_free`. |
| `FIO_REF_CONSTRUCTOR_ONLY` | Generates `REF_new`, `REF_dup`, `REF_free` — use this when the ref-counted constructor **is** the primary constructor. |
### Object lifecycle hooks
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_INIT(obj)` | zero-fill if allocator doesn't guarantee it | Called on the newly allocated `FIO_REF_TYPE` object after allocation. `obj` is the dereferenced object (not a pointer). |
| `FIO_REF_DESTROY(obj)` | _(nothing)_ | Called on the `FIO_REF_TYPE` object just before memory is freed (when the last reference drops). |
### Metadata
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_METADATA` | _(not defined)_ | A type to embed as hidden metadata alongside the ref-counted object. Access it via `REF_metadata()`. |
| `FIO_REF_METADATA_INIT(meta)` | zero-fill if needed | Called on the metadata field after allocation. |
| `FIO_REF_METADATA_DESTROY(meta)` | _(nothing)_ | Called on the metadata field just before memory is freed. |
### Flexible array
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_FLEX_TYPE` | _(not defined)_ | When defined, the constructor allocates extra memory for a `FIO_REF_FLEX_TYPE[]` array immediately after the main struct. The constructor accepts a `size_t members` argument and stores the count internally. The `members` value is also available inside `FIO_REF_INIT`. |
> **Note:** `FIO_REF_FLEX_TYPE` shrinks the reference counter to 32 bits (instead of the native word size). Do not combine this with a custom `FIO_MEM_FREE` that depends on the byte-count argument, because the flex free call reports a size that omits `sizeof(FIO_REF_TYPE)`.
### Pointer tagging
If `FIO_PTR_TAG_TYPE` is defined (globally, before including), all generated functions accept and return `FIO_PTR_TAG_TYPE` instead of `FIO_REF_TYPE *`. Tags are applied on allocation (`FIO_PTR_TAG`) and stripped internally (`FIO_PTR_UNTAG`).
---
## Generated API
### Constructor
```c
/* standard */
FIO_REF_TYPE *REF_new(void); /* FIO_REF_CONSTRUCTOR_ONLY */
FIO_REF_TYPE *REF_new2(void); /* default */
/* with FIO_REF_FLEX_TYPE */
FIO_REF_TYPE *REF_new(size_t members);
FIO_REF_TYPE *REF_new2(size_t members);
```
Allocates and zero-initializes the wrapper header, calls `FIO_REF_METADATA_INIT` (if any), then calls `FIO_REF_INIT` on the wrapped object. Returns `NULL` on allocation failure. Initial reference count is **1**.
### Increment reference count
```c
FIO_REF_TYPE *REF_dup(const FIO_REF_TYPE *obj); /* FIO_REF_CONSTRUCTOR_ONLY */
FIO_REF_TYPE *REF_dup2(const FIO_REF_TYPE *obj); /* default */
```
Atomically increments the reference count. Returns the same pointer, or `NULL` if the input is `NULL`. Thread-safe.
### Decrement reference count / free
```c
void REF_free(FIO_REF_TYPE *obj); /* FIO_REF_CONSTRUCTOR_ONLY */
void REF_free2(FIO_REF_TYPE *obj); /* default */
```
Atomically decrements the reference count. When the count reaches zero, calls `FIO_REF_DESTROY(object)`, then `FIO_REF_METADATA_DESTROY(metadata)` (if any), then frees the backing memory. Thread-safe. No-op on `NULL`.
### Metadata access
```c
FIO_REF_METADATA *REF_metadata(FIO_REF_TYPE *obj);
```
Returns a pointer to the hidden metadata field. Only generated when `FIO_REF_METADATA` is defined.
### Flex array length
```c
uint32_t REF_metadata_flex_len(FIO_REF_TYPE *obj);
```
Returns the number of `FIO_REF_FLEX_TYPE` members allocated alongside the object. Only generated when `FIO_REF_FLEX_TYPE` is defined. Returns `0` for `NULL`.
### Debugging helper
```c
size_t REF_references(FIO_REF_TYPE *obj);
```
Returns the current reference count. **Do not use for program logic** — the value is inherently unstable in a concurrent context. Useful for assertions and leak-hunting.
---
## Examples
### Basic reference-counted object
```c
typedef struct { int x; int y; } point_s;
#define FIO_REF_NAME point
#define FIO_REF_TYPE point_s
#define FIO_REF_CONSTRUCTOR_ONLY /* point_new / point_dup / point_free */
#define FIO_REF_INIT(obj) (obj).x = 0; (obj).y = 0
#include "fio-stl.h"
void example(void) {
point_s *p = point_new(); /* ref count = 1 */
p->x = 10; p->y = 20;
point_s *alias = point_dup(p); /* ref count = 2 */
point_free(alias); /* ref count = 1 — not freed yet */
point_free(p); /* ref count = 0 — freed */
}
```
### Using the `2`-suffix when a primary constructor already exists
```c
/* Suppose FIO_STR_NAME already generated str_new / str_free. */
#define FIO_REF_NAME str
#define FIO_REF_TYPE str_s
/* No FIO_REF_CONSTRUCTOR_ONLY — generates str_new2 / str_dup2 / str_free2 */
#define FIO_REF_DESTROY(obj) str_destroy(&(obj))
#include "fio-stl.h"
str_s *s = str_new2(); /* ref-counted allocation */
str_write(s, "hello", 5); /* use the string API directly */
str_s *s2 = str_dup2(s);
str_free2(s2);
str_free2(s); /* calls str_destroy, then frees memory */
```
### Hidden metadata
```c
typedef struct { char name[64]; } widget_s;
typedef struct { uint64_t created_at; } widget_meta_s;
#define FIO_REF_NAME widget
#define FIO_REF_TYPE widget_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_METADATA widget_meta_s
#define FIO_REF_METADATA_INIT(m) (m).created_at = fio_time_real().tv_sec
#include "fio-stl.h"
void example(void) {
widget_s *w = widget_new();
widget_meta_s *m = widget_metadata(w);
printf("created at: %llu\n", (unsigned long long)m->created_at);
widget_free(w);
}
```
### Flexible array suffix
```c
typedef struct { size_t len; double data[]; } vec_s;
#define FIO_REF_NAME vec
#define FIO_REF_TYPE vec_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_FLEX_TYPE double
#define FIO_REF_INIT(obj) (obj).len = members /* 'members' is in scope */
#include "fio-stl.h"
void example(void) {
vec_s *v = vec_new(8); /* allocates vec_s + 8 doubles */
uint32_t n = vec_metadata_flex_len(v); /* returns 8 */
for (uint32_t i = 0; i < n; i++) v->data[i] = (double)i;
vec_free(v);
}
```
---
## Memory Layout
```
[ _wrapper_s header ] [ FIO_REF_TYPE object ] [ FIO_REF_FLEX_TYPE[] ] (optional)
^ ^
hidden prefix pointer returned to caller
```
The `_wrapper_s` header holds the reference count (and optional `flx_size` / `metadata` fields). Callers never see it — they only receive a pointer to the `FIO_REF_TYPE` that immediately follows.
The header's declared alignment matches `_Alignof(FIO_REF_TYPE)`, so its size rounds up to a multiple of that alignment and the object that follows it is always correctly aligned. Allocation goes through `FIO_MEM_REALLOC_ALIGNED_` with the type's alignment (the allocator treats smaller-than-default requests as the default), so over-aligned types work in every allocator configuration.
---
## Notes
- All reference-count operations use **atomic** instructions and are thread-safe.
- The module must be placed after any other type modules it wraps (e.g., after `FIO_STR_NAME` or `FIO_ARRAY_NAME` inclusions) when used together in one translation unit.
- Each `FIO_REF_NAME` definition is independent: you can wrap multiple types in the same file.
- `REF_references()` is a debugging aid; its return value is transient and must not drive control flow.
# FIOBJ — Soft Dynamic Types — Module 250
A unified soft-type system built on pointer tagging. One `FIOBJ` handle covers `null`, `true`, `false`, integers, floats, strings, arrays, and hash maps — plus user-defined extensions.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_FIOBJ
#include "fio-stl.h"
```
No name macro is needed — `FIO_FIOBJ` activates the entire soft-type system in one step.
To expose symbols across multiple translation units add `FIO_EXTERN` before the include everywhere, and `FIO_EXTERN_COMPLETE` in exactly one `.c` file.
---
## How it Works
`FIOBJ` is a tagged pointer (`uintptr_t`). The bottom 3 bits encode the type class; the remaining bits hold the value or a heap pointer. Small integers and many floats require **no heap allocation** at all.
```
bits[2:0] type class
bits[63:3] value or aligned pointer
```
All heap-allocated types are reference-counted. Primitives, small numbers, and many floats are immediate values — `fiobj_dup` / `fiobj_free` are no-ops for them.
The system uses an **ownership** model:
- Placing a value into an Array or Hash Map **transfers ownership** to the container.
- Freeing the container frees its elements (Arrays own members; Hash Maps own keys and values).
- Call `fiobj_dup` before inserting if you need to keep an independent reference.
---
## Configuration Macros
Set these before `#include` to override defaults.
### Nesting / recursion
| Macro | Default | Effect |
|---|---|---|
| `FIOBJ_MAX_NESTING` | `512` | Maximum depth for `fiobj_each2` and JSON serialization/parsing. Does **not** apply to `fiobj_free`. |
| `FIOBJ_JSON_APPEND` | `1` | When `1`, JSON parsing appends to existing containers; when `0`, it replaces them. |
### Type-name prefixes
All generated type names can be renamed by defining the following macros before the include. The defaults produce the API names used throughout this document.
| Macro | Default | Resulting prefix |
|---|---|---|
| `FIOBJ___NAME_NULL` | `null` | `fiobj_null` |
| `FIOBJ___NAME_NUMBER` | `num` | `fiobj_num_*` |
| `FIOBJ___NAME_FLOAT` | `float` | `fiobj_float_*` |
| `FIOBJ___NAME_STRING` | `str` | `fiobj_str_*` |
| `FIOBJ___NAME_ARRAY` | `array` | `fiobj_array_*` |
| `FIOBJ___NAME_HASH` | `hash` | `fiobj_hash_*` |
---
## Type Identification
### Type constants
| Constant | Value | Description |
|---|---|---|
| `FIOBJ_T_INVALID` | `0` | Invalid / uninitialized FIOBJ |
| `FIOBJ_T_PRIMITIVE` | `2` | Raw pointer-tag class for all primitives |
| `FIOBJ_T_NULL` | `2` | `null` primitive |
| `FIOBJ_T_TRUE` | `18` | `true` primitive |
| `FIOBJ_T_FALSE` | `34` | `false` primitive |
| `FIOBJ_T_NUMBER` | `0x01` | Integer (small integers are unboxed) |
| `FIOBJ_T_FLOAT` | `0x06` | Float / double (many values are unboxed) |
| `FIOBJ_T_STRING` | `0x03` | Dynamic string |
| `FIOBJ_T_ARRAY` | `0x04` | Dynamic array |
| `FIOBJ_T_HASH` | `0x05` | Ordered hash map |
| `FIOBJ_T_OTHER` | `0x07` | Extension / user-defined type |
| `FIOBJ_INVALID` | `0` | The invalid FIOBJ sentinel value |
### Type-check macros
```c
size_t FIOBJ_TYPE(FIOBJ o); /* full type ID, including extensions */
int FIOBJ_TYPE_IS(FIOBJ o, type); /* 1 if type matches */
fiobj_class_en FIOBJ_TYPE_CLASS(FIOBJ o); /* raw 3-bit class (no extension lookup) */
int FIOBJ_IS_INVALID(FIOBJ o); /* true if o == FIOBJ_INVALID (0) */
int FIOBJ_IS_NULL(FIOBJ o); /* true if INVALID or null primitive */
```
`FIOBJ_TYPE` is the safe, recommended way to check type. `FIOBJ_TYPE_CLASS` is faster but may return `FIOBJ_T_OTHER` for heap-boxed numbers and floats. It also returns `FIOBJ_T_PRIMITIVE` (`2`) for `null`, `true`, and `false` alike — use `FIOBJ_TYPE` to distinguish primitives.
---
## Lifecycle — Reference Counting
```c
FIOBJ fiobj_dup(FIOBJ o); /* increment ref count; returns o */
void fiobj_free(FIOBJ o); /* decrement ref count; frees when zero */
```
Primitives, unboxed integers, and unboxed floats are immune — `fiobj_dup` / `fiobj_free` are no-ops for them. Always call `fiobj_free` on every FIOBJ you own.
**Warning:** `fiobj_free` is recursive. Deeply nested structures can overflow the stack. `FIOBJ_MAX_NESTING` does **not** protect it. Use the JSON parser (which does protect against nesting attacks) when handling untrusted input.
```c
/* build a small object graph */
FIOBJ arr = fiobj_array_new();
fiobj_array_push(arr, fiobj_num_new(42)); /* array owns the number */
fiobj_array_push(arr, fiobj_str_new_cstr("hi", 2)); /* array owns the string */
FIOBJ ref = fiobj_dup(arr); /* arr and ref both point to same array */
fiobj_free(ref); /* decrement; array still alive */
fiobj_free(arr); /* decrement to zero → frees array + contents */
```
---
## Generic Accessors
These work on **any** FIOBJ type.
```c
fio_str_info_s fiobj2cstr(FIOBJ o); /* string representation (temporary) */
intptr_t fiobj2i(FIOBJ o); /* integer representation */
double fiobj2f(FIOBJ o); /* float representation */
uint64_t fiobj2hash(FIOBJ o); /* hash value (for use as map key) */
unsigned char fiobj_is_eq(FIOBJ a, FIOBJ b); /* deep equality */
```
Conversion behaviour by type:
| Object type | `fiobj2i` | `fiobj2f` | `fiobj2cstr` |
|---|---|---|---|
| `null` / invalid | `0` | `0.0` | `"null"` / `""` |
| `true` | `1` | `1.0` | `"true"` |
| `false` | `0` | `0.0` | `"false"` |
| Number | the integer | cast to double | base-10 string |
| Float | floor to integer | the double | decimal string |
| String | `fio_atol` of content | `fio_atof` of content | raw content |
| Array | element count | element count as double | `"[...]"` |
| Hash | key-value pair count | count as double | `"{...}"` |
`fiobj_is_eq` performs deep structural comparison for Arrays and Hash Maps.
`fiobj2cstr` returns a temporary view. For numbers/floats the buffer is thread-local (safe for up to 128 concurrent threads). For heap strings it points directly into the string's buffer.
---
## Primitives — null, true, false
The three primitives are immediate constants. They never allocate memory and never need `fiobj_free`.
```c
FIOBJ fiobj_null(void); /* returns the null primitive */
FIOBJ fiobj_true(void); /* returns the true primitive */
FIOBJ fiobj_false(void); /* returns the false primitive */
```
```c
FIOBJ v = fiobj_null();
if (FIOBJ_IS_NULL(v)) /* also true for FIOBJ_INVALID (0) */
printf("got null\n");
if (FIOBJ_TYPE_IS(v, FIOBJ_T_NULL)) /* strictly the null primitive */
printf("strict null\n");
```
---
## Numbers — `fiobj_num_*`
Small integers are stored in the tagged pointer itself — no allocation. Large values that don't fit are heap-boxed transparently.
```c
FIOBJ fiobj_num_new(intptr_t i); /* create */
intptr_t fiobj_num2i(FIOBJ i); /* read as integer */
double fiobj_num2f(FIOBJ i); /* read as double */
fio_str_info_s fiobj_num2cstr(FIOBJ i); /* base-10 string (temp) */
void fiobj_num_free(FIOBJ i); /* type-specific free */
```
`fiobj_num_free` is a fast alternative to `fiobj_free` when the type has already been validated.
---
## Floats — `fiobj_float_*`
Many double values fit in the tagged pointer (those whose lower 3 bits of the IEEE 754 representation are zero). Others are heap-boxed.
```c
FIOBJ fiobj_float_new(double i); /* create */
intptr_t fiobj_float2i(FIOBJ i); /* floor to integer */
double fiobj_float2f(FIOBJ i); /* read as double */
fio_str_info_s fiobj_float2cstr(FIOBJ i); /* decimal string (temp) */
void fiobj_float_free(FIOBJ i); /* type-specific free */
```
---
## Strings — `fiobj_str_*`
FIOBJ strings wrap the [Dynamic String module (201)](./201 string.md). The full `fiobj_str_*` API mirrors the `STR_*` API — write, concat, printf, base64, JSON-escape, UTF-8 helpers, freeze, etc.
### Constructors
```c
FIOBJ fiobj_str_new(void); /* empty string */
FIOBJ fiobj_str_new_cstr(const char *ptr, size_t len); /* copy from C string */
FIOBJ fiobj_str_new_buf(size_t capa); /* empty with reserved capacity */
FIOBJ fiobj_str_new_copy(FIOBJ original); /* copy from any FIOBJ (via fiobj2cstr) */
```
### Common operations
```c
fio_str_info_s fiobj_str2cstr(FIOBJ s); /* same as fiobj_str_info() */
void fiobj_str_write(FIOBJ s, const char *buf, size_t len);
void fiobj_str_printf(FIOBJ s, const char *fmt, ...);
void fiobj_str_concat(FIOBJ dest, FIOBJ src); /* append src string */
void fiobj_str_freeze(FIOBJ s); /* mark immutable */
void fiobj_str_free(FIOBJ s); /* type-specific free */
```
### Stack-allocated temporary strings
```c
/* Empty temporary string on the stack */
FIOBJ_STR_TEMP_VAR(name);
/* Temporary string wrapping a static buffer (read-only view) */
FIOBJ_STR_TEMP_VAR_STATIC(name, buf, len);
/* Temporary string wrapping an existing read/write buffer */
FIOBJ_STR_TEMP_VAR_EXISTING(name, buf, len, capa);
/* Always call when done — releases any dynamic allocation */
FIOBJ_STR_TEMP_DESTROY(name);
```
Temporary strings live on the stack and are valid FIOBJ values. They must **not** be passed to containers or freed with `fiobj_free`.
```c
/* example: use a stack string as a hash key without heap allocation */
FIOBJ_STR_TEMP_VAR_STATIC(key, "name", 4);
FIOBJ val = fiobj_hash_get(hash, key);
FIOBJ_STR_TEMP_DESTROY(key);
```
---
## Arrays — `fiobj_array_*`
FIOBJ arrays wrap the [Dynamic Array module (202)](./202 array.md). The element type is `FIOBJ`; elements are owned by the array.
### Common operations
```c
FIOBJ fiobj_array_new(void);
void fiobj_array_free(FIOBJ a);
FIOBJ *fiobj_array_push(FIOBJ a, FIOBJ value); /* append; transfers ownership; NULL on error */
int fiobj_array_pop(FIOBJ a, FIOBJ *old); /* remove last; stores in *old or frees if NULL */
FIOBJ fiobj_array_get(FIOBJ a, int64_t index); /* access; NO ownership transfer */
FIOBJ *fiobj_array_set(FIOBJ a, int64_t index, FIOBJ value, FIOBJ *old); /* old in *old or freed */
int fiobj_array_remove(FIOBJ a, int64_t index, FIOBJ *old); /* remove; optional ownership transfer */
uint32_t fiobj_array_count(FIOBJ a);
void fiobj_array_concat(FIOBJ dest, FIOBJ src); /* appends copies of src elements */
```
Negative indices count from the end (`-1` = last element).
```c
FIOBJ a = fiobj_array_new();
fiobj_array_push(a, fiobj_num_new(1));
fiobj_array_push(a, fiobj_num_new(2));
fiobj_array_push(a, fiobj_str_new_cstr("three", 5));
printf("count: %u\n", fiobj_array_count(a)); /* 3 */
printf("first: %ld\n", fiobj2i(fiobj_array_get(a, 0))); /* 1 */
fiobj_free(a); /* frees array and all three elements */
```
---
## Hash Maps — `fiobj_hash_*`
FIOBJ hash maps are **ordered** maps keyed and valued by FIOBJ. They wrap the [Hash Map module (210)](./210 map.md). The map owns stored keys and values. The low-level `fiobj_hash_set` duplicates FIOBJ keys, so the caller still owns its original key reference; C-string helpers copy the key string internally.
### Core operations
```c
FIOBJ fiobj_hash_new(void);
void fiobj_hash_free(FIOBJ h);
uint32_t fiobj_hash_count(FIOBJ h);
/* FIOBJ-key variants (low level) */
FIOBJ fiobj_hash_set(FIOBJ h, FIOBJ key, FIOBJ value, FIOBJ *old);
FIOBJ fiobj_hash_get(FIOBJ h, FIOBJ key);
int fiobj_hash_remove(FIOBJ h, FIOBJ key, FIOBJ *old);
```
### C-string key helpers (recommended)
```c
/* set: copies key string, transfers value ownership */
FIOBJ fiobj_hash_set2(FIOBJ h, const char *key, size_t len, FIOBJ value);
/* get: no allocation, no ownership transfer */
FIOBJ fiobj_hash_get2(FIOBJ h, const char *key, size_t len);
/* remove: returns 0 on success; puts old value in *old (or frees it if NULL) */
int fiobj_hash_remove2(FIOBJ h, const char *key, size_t len, FIOBJ *old);
```
### Merge
```c
/* deep merge: arrays concatenate, nested hashes recurse, scalars overwrite;
null/invalid values in src remove the matching key from dest. */
void fiobj_hash_update(FIOBJ dest, FIOBJ src);
```
```c
FIOBJ h = fiobj_hash_new();
fiobj_hash_set2(h, "name", 4, fiobj_str_new_cstr("Alice", 5));
fiobj_hash_set2(h, "age", 3, fiobj_num_new(30));
FIOBJ name = fiobj_hash_get2(h, "name", 4);
printf("%s\n", fiobj2cstr(name).buf); /* Alice */
fiobj_free(h); /* frees map, values, and key strings */
```
---
## Iteration
### Shallow — `fiobj_each1`
Iterates direct children of an Array or Hash Map.
```c
typedef struct fiobj_each_s {
FIOBJ const parent; /* the container being iterated */
uint64_t index; /* current element index */
int (*task)(struct fiobj_each_s *info); /* callback (may be swapped mid-loop) */
void *udata; /* caller-supplied data */
FIOBJ value; /* current element value */
FIOBJ key; /* current key (Hash Maps only) */
} fiobj_each_s;
uint32_t fiobj_each1(FIOBJ o,
int (*task)(fiobj_each_s *info),
void *udata,
int32_t start_at);
```
Return `-1` from the callback to stop early. Returns the stop position (`elements processed + start_at`).
```c
int print_item(fiobj_each_s *e) {
printf("[%llu] %s\n", (unsigned long long)e->index,
fiobj2cstr(e->value).buf);
return 0;
}
fiobj_each1(arr, print_item, NULL, 0);
```
### Deep — `fiobj_each2`
Recursively walks the object and all nested containers, as if flattened.
```c
uint32_t fiobj_each2(FIOBJ o,
int (*task)(fiobj_each_s *info),
void *udata);
```
Respects `FIOBJ_MAX_NESTING`. The root object itself is also passed to the callback (`index == 0`, `parent == FIOBJ_INVALID`).
---
## JSON
### Serialize to JSON
```c
FIOBJ fiobj2json(FIOBJ dest, FIOBJ o, uint8_t beautify);
```
Returns a `FIOBJ` string containing the JSON representation of `o`. If `dest` is an existing FIOBJ string, the JSON is **appended** to it; otherwise a new string is created. Pass `beautify != 0` for indented output.
```c
FIOBJ obj = fiobj_json_parse2("{\"x\":1,\"y\":[2,3]}", 18, NULL);
FIOBJ json = fiobj2json(FIOBJ_INVALID, obj, 1); /* beautified */
printf("%s\n", fiobj2cstr(json).buf);
fiobj_free(json);
fiobj_free(obj);
```
### Parse JSON
```c
/* parse a full JSON value */
FIOBJ fiobj_json_parse(fio_str_info_s str, size_t *consumed);
/* convenience macro */
#define fiobj_json_parse2(data, len, consumed) \
fiobj_json_parse(FIO_STR_INFO2(data, len), consumed)
```
Returns `FIOBJ_INVALID` on parse error. If `consumed` is non-NULL it is set to the number of bytes read.
### Merge JSON into a Hash Map
```c
size_t fiobj_hash_update_json(FIOBJ hash, fio_str_info_s str);
size_t fiobj_hash_update_json2(FIOBJ hash, char *ptr, size_t len);
```
Parses JSON and merges the resulting key-value pairs into an existing hash map. Silently skips non-object JSON data. Returns bytes consumed (0 on error).
### JavaScript-style path lookup
```c
FIOBJ fiobj_json_find(FIOBJ object, fio_str_info_s notation);
#define fiobj_json_find2(object, str, length) \
fiobj_json_find(object, FIO_STR_INFO2(str, length))
```
Finds a nested value using dot/bracket notation. For example `"[0].name"` returns the `name` field of the first array element. Returns a **temporary reference** — call `fiobj_dup` if you need to keep it.
```c
FIOBJ data = fiobj_json_parse2("[{\"name\":\"Bob\"}]", 16, NULL);
FIOBJ name = fiobj_json_find2(data, "[0].name", 8);
printf("%s\n", fiobj2cstr(name).buf); /* Bob */
fiobj_free(data);
/* name was a temporary reference into data; do not free separately */
```
---
## Mustache Templates
Renders a compiled Mustache template using FIOBJ data (typically a Hash Map) as context.
```c
/* create a new FIOBJ string with the rendered output */
FIOBJ fiobj_mustache_build(fio_mustache_s *m, FIOBJ ctx);
/* append rendered output to an existing FIOBJ string */
FIOBJ fiobj_mustache_build2(fio_mustache_s *m, FIOBJ dest, FIOBJ ctx);
```
Both may return `FIOBJ_INVALID` if nothing was written and `dest` was empty/invalid. See the Mustache module for how to compile a template (`fio_mustache_s *`).
---
## Extending FIOBJ
Custom types integrate via a virtual function table tagged as `FIOBJ_T_OTHER`.
### Requirements
1. Choose a **unique** type ID ≥ 100. Values below 100 are reserved; values below 40 are illegal.
2. Populate a `FIOBJ_class_vtable_s` — all function pointers must be non-NULL.
3. Wrap the type with `FIO_REF_NAME` (see [249 reference counter.md](./249 reference counter.md)), setting `FIO_REF_METADATA` to `const FIOBJ_class_vtable_s *` and initialising it in `FIO_REF_METADATA_INIT`.
4. Apply pointer tagging so all pointers carry `FIOBJ_T_OTHER` in the low 3 bits.
```c
#define FIO_PTR_TAG(p) FIOBJ_PTR_TAG(p, FIOBJ_T_OTHER)
#define FIO_PTR_UNTAG(p) FIOBJ_PTR_UNTAG(p)
#define FIO_PTR_TAG_TYPE FIOBJ
```
### Virtual function table
```c
typedef struct {
size_t type_id; /* unique ID ≥ 100 */
unsigned char (*is_eq)(FIOBJ a, FIOBJ b);
fio_str_info_s (*to_s)(FIOBJ o);
intptr_t (*to_i)(FIOBJ o);
double (*to_f)(FIOBJ o);
uint32_t (*count)(FIOBJ o); /* number of child elements */
uint32_t (*each1)(FIOBJ o, int (*task)(fiobj_each_s *), void *udata, int32_t start_at);
void (*free2)(FIOBJ o); /* free when ref count reaches zero */
} FIOBJ_class_vtable_s;
```
All slots must be filled. If the type has no children, `count` should return `0` and `each1` should be a no-op that returns `0`.
### Minimal example
```c
#define FIOBJ_T_POINT 100UL
typedef struct { double x, y; } point_data_s;
static unsigned char point_eq(FIOBJ a, FIOBJ b) { /* ... */ return 0; }
static fio_str_info_s point_to_s(FIOBJ o) { /* ... */ return (fio_str_info_s){0}; }
static intptr_t point_to_i(FIOBJ o) { return 0; }
static double point_to_f(FIOBJ o) { return 0.0; }
static uint32_t point_count(FIOBJ o) { return 0; (void)o; }
static uint32_t point_each1(FIOBJ o, int (*t)(fiobj_each_s *), void *u, int32_t s)
{ return 0; (void)o;(void)t;(void)u;(void)s; }
static void point_free2(FIOBJ o); /* forward declaration */
static const FIOBJ_class_vtable_s FIOBJ___POINT_VTBL = {
.type_id = FIOBJ_T_POINT,
.is_eq = point_eq, .to_s = point_to_s,
.to_i = point_to_i, .to_f = point_to_f,
.count = point_count, .each1 = point_each1,
.free2 = point_free2,
};
#define FIO_REF_NAME fiobj_point
#define FIO_REF_TYPE point_data_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_METADATA const FIOBJ_class_vtable_s *
#define FIO_REF_METADATA_INIT(m) (m = &FIOBJ___POINT_VTBL)
#define FIO_PTR_TAG(p) FIOBJ_PTR_TAG(p, FIOBJ_T_OTHER)
#define FIO_PTR_UNTAG(p) FIOBJ_PTR_UNTAG(p)
#define FIO_PTR_TAG_TYPE FIOBJ
#include "fio-stl.h"
static void point_free2(FIOBJ o) { fiobj_point_free(o); }
FIOBJ fiobj_point_new_xy(double x, double y) {
FIOBJ o = fiobj_point_new();
point_data_s *p = (point_data_s *)FIOBJ_PTR_UNTAG(o);
p->x = x; p->y = y;
return o;
}
```
---
## Debugging / Leak Detection
When compiled with `TEST`, `DEBUG`, or `FIO_LEAK_COUNTER` defined, two global counters track allocations:
```c
size_t FIOBJ_MARK_MEMORY_ALLOC_COUNTER;
size_t FIOBJ_MARK_MEMORY_FREE_COUNTER;
```
Call `FIOBJ_MARK_MEMORY_PRINT()` after all objects should be freed to log any leaks.
# IO and HTTP — Group Overview (400-range)
```c
#define FIO_IO /* IO Reactor */
#define FIO_IPC /* Inter-Process Communication */
#define FIO_PUBSUB /* Pub/Sub */
#define FIO_REDIS /* Redis engine */
#define FIO_HTTP /* HTTP server / client */
#include "fio-stl.h"
```
The 400-range is where facil.io becomes a networked server. A single-threaded
evented IO reactor sits at the foundation, optional TLS plugs into it as a
transport hook, IPC ferries work between worker processes, PubSub builds
broadcast semantics on top of IPC, Redis extends PubSub across machines, and
HTTP closes the loop with a production-grade server that speaks HTTP/1.x,
WebSocket, and SSE.
---
## Stack Diagram
```
┌──────────────────────────────────────────────────────────────────┐
│ HTTP (439 http.h) │
│ fio_http_listen · fio_http_connect │
│ HTTP/1.x · WebSocket upgrades · SSE (EventSource) │
└──────┬─────────────────────────────────────────┬─────────────────┘
│ uses (internal) │ optional
▼ ▼
┌──────────────────────────────┐ ┌─────────────────────────────┐
│ HTTP parsers (004) │ │ Pub/Sub (420) │
│ · HTTP/1.1 parser │ │ subscribe · publish │
│ · WebSocket parser (RFC6455) │ │ pattern matching · replay │
│ · HTTP Handle (internal) │ │ pluggable engine interface │
└──────────────────────────────┘ └──────────────┬──────────────┘
│ engine
┌──────────────▼──────────────┐
│ Redis (422 redis.h) │
│ connects via IO (master) │
│ workers route through IPC │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ IPC (404 ipc.h) │
│ worker ↔ master messages │
│ cluster RPC (multi-machine) │
│ encrypted: ChaCha20-Poly1305│
└──────────────┬──────────────┘
│ built on
┌───────────────────────────────────────────────────▼──────────────┐
│ IO Reactor (401–403) │
│ fio_io_s · fio_io_protocol_s epoll / kqueue / poll │
│ fio_io_listen · fio_io_connect fio_io_defer · fio_io_run_every │
│ fio_io_async_s (background thread pools for non-IO tasks) │
└──────────────────────────────────┬───────────────────────────────┘
│ transport hooks (fio_io_functions_s)
▼
┌─────────────────────────┐
│ TLS (405) │
│ OpenSSL 3.x (preferred) │
│ or native TLS 1.3 │
│ auto-selected at build │
└─────────────────────────┘
```
---
## Layer-by-Layer
### IO Reactor — 401 · 402 · 403
**Enable with:** `#define FIO_IO`
**Docs:** [./401 io api.md](./401 io api.md)
The event loop. `fio_io_start(workers)` blocks and runs forever (or until
`fio_io_stop()`). With `workers > 0` it forks child processes; the master stays
alive to manage IPC and listeners while workers handle connections.
Key types and functions:
- `fio_io_s` — opaque IO handle (connection). Never store raw; use
`fio_io_dup` / `fio_io_free` for lifetime management.
- `fio_io_protocol_s` — struct of callbacks (`on_attach`, `on_data`,
`on_ready`, `on_shutdown`, `on_timeout`, `on_close`) shared by all
connections of the same protocol family.
- `fio_io_listen(...)` — attach a listening socket with a protocol.
- `fio_io_connect(...)` — connect as a client.
- `fio_io_attach_fd(fd, protocol, udata, tls)` — adopt a raw fd.
- `fio_io_read / fio_io_write / fio_io_close` — IO operations (safe only
inside callbacks or deferred tasks).
- `fio_io_defer(task, u1, u2)` — schedule a task on the IO thread (thread-safe).
- `fio_io_run_every(...)` — schedule a recurring timer.
`fio_io_async_s` / `fio_io_async_attach(q, threads)` spins up a background
thread pool for CPU-heavy or blocking work that must not block the IO loop.
---
### TLS — 405 openssl.h · 405 tls13.h
**Enable with:** `#define FIO_IO` (TLS backends auto-include when available)
**Docs:** [./405 openssl.md](./405 openssl.md) · [./405 tls13.md](./405 tls13.md) *(planned)*
TLS is plugged into the reactor through `fio_io_functions_s` — a set of
transport callbacks (`build_context`, `free_context`, `start`, `read`, `write`,
`flush`, `finish`, `cleanup`) that replace the default socket calls.
The `fio_io_tls_s` context carries certificates, ALPN negotiation entries, and
trusted CA chains. It is built from `fio_io_tls_new()` / `fio_io_tls_cert_add()`
/ `fio_io_tls_alpn_add()` / `fio_io_tls_trust_add()`, or parsed directly from a
URL query string:
```c
fio_io_listen(.url = "0.0.0.0:443/?tls=./certs/", .protocol = &my_proto);
```
Two backends are available — OpenSSL 3.x (`fio_openssl_io_functions()`) and
the native TLS 1.3 engine (`fio_tls13_io_functions()`). The default is
selected automatically at build time; override with
`fio_io_tls_default_functions(&my_io_functions)` before `fio_io_start`.
> **Disambiguation:** `405` documents IO-layer TLS integration — how TLS wraps
> network connections in the reactor. The standalone TLS 1.3 crypto library
> (key schedule, record layer, handshake state machines, certificate helpers)
> lives separately at [./190 tls13.md](./190 tls13.md) and is what `405` builds on.
---
### IPC — 404 ipc.h
**Enable with:** `#define FIO_IPC`
**Docs:** [./404 ipc.md](./404 ipc.md) *(planned)*
Workers need to talk to the master. IPC handles this over a Unix socket (or
optionally TCP for multi-machine clusters). Messages are reference-counted,
encrypted in transit (ChaCha20-Poly1305, keyed from the process secret), and
routed by flags:
| Macro | Delivery |
|-------|----------|
| `fio_ipc_call(...)` | master only |
| `fio_ipc_local(...)` | master + all local workers |
| `fio_ipc_cluster(...)` | master on every machine |
| `fio_ipc_broadcast(...)` | master + workers on every machine |
For cross-machine RPC, register a non-zero op-code with
`fio_ipc_opcode_register(...)` before `fio_io_start`. Function-pointer messages
("fast path") work only within a single machine.
---
### Pub/Sub — 420 pubsub.h
**Enable with:** `#define FIO_PUBSUB` (requires `FIO_IPC`)
**Docs:** [./420 pubsub.md](./420 pubsub.md) *(planned)*
Pub/Sub delivers messages to named channels across all workers and, via
registered engines, across machines. The core API:
```c
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = my_callback,
.io = client_io);
fio_pubsub_publish(.channel = FIO_BUF_INFO1("news"),
.message = FIO_BUF_INFO1("breaking!"));
```
Subscriptions can be owned by an `fio_io_s` (auto-cancelled on close) or by
the global environment. Pattern subscriptions and numeric namespace `filter`
values are supported. An `engine` field selects an external backend (Redis,
custom). Replay from a timestamp is supported when history engines are attached.
---
### Redis — 422 redis.h
**Enable with:** `#define FIO_REDIS` (requires `FIO_PUBSUB`)
**Docs:** [./422 redis.md](./422 redis.md) *(planned)*
Redis integration as a PubSub engine *and* as a standalone command client.
Only the master process connects to the Redis server — workers route all
commands and publishes through IPC. This avoids multiplying Redis connections
and preserves correct subscribe/unsubscribe semantics.
```c
/* attach as a pub/sub engine */
fio_pubsub_engine_s *r = fio_redis_new(.url = "redis://localhost:6379");
/* send arbitrary Redis commands (not SUBSCRIBE/UNSUBSCRIBE) */
fio_redis_send(r, my_fiobj_command_array, my_reply_cb, NULL);
```
The return type is `fio_pubsub_engine_s *` — attach it to a pub/sub
subscription via the `engine` field. Reference counting
(`fio_redis_dup` / `fio_redis_free`) governs the engine lifetime.
All mutation runs on the IO thread via `fio_io_defer`.
---
### HTTP Parsers — 004 http1 parser.h · 004 websocket parser.h
**Enable with:** `#define FIO_HTTP1_PARSER` / `#define FIO_WEBSOCKET_PARSER`
**Docs:** [./004 http1 parser.md](./004 http1 parser.md) · [./004 websocket parser.md](./004 websocket parser.md)
Zero-allocation, event-driven parsers — no internal buffering, no heap.
- **HTTP/1.1 parser** (`fio_http1_parse`): parses request/response lines,
headers, and bodies (including chunked). Fires user-implemented callbacks
(`fio_http1_on_method`, `fio_http1_on_url`, `fio_http1_on_header`,
`fio_http1_on_body_chunk`, `fio_http1_on_complete`, …).
- **WebSocket parser** (`fio_websocket_parse`): RFC 6455 frame parser.
Zero-allocation, cache-sized. Produces typed events
(`FIO_WEBSOCKET_EV_DATA_CHUNK`, `FIO_WEBSOCKET_EV_CONTROL`,
`FIO_WEBSOCKET_EV_MESSAGE_END`, `FIO_WEBSOCKET_EV_ERROR`).
These are the building blocks used internally by the HTTP layer. Direct use
is for protocol-level work or embedding the parsers in a custom IO protocol.
**HTTP Handle** (internal — core in `432 http types.h`) is the
`fio_http_s` request/response state object with header
cache, body (RAM or file), and logging support. The handle is fully covered by the
HTTP documentation; there is no separate public handle doc.
---
### HTTP — 439 http.h
**Enable with:** `#define FIO_HTTP`
**Docs:** [./439 http.md](./439 http.md)
The top-level server and client. `fio_http_listen` registers an HTTP service
on the IO reactor; `fio_http_connect` opens an HTTP client connection.
```c
fio_http_listen("0.0.0.0:3000", .on_http = handle_request);
fio_io_start(0); /* or fio_io_start(4) for 4 worker processes */
```
The `fio_http_settings_s` struct wires up:
- `on_http` — HTTP request/response callback
- `on_open` / `on_message` / `on_close` — WebSocket lifecycle
- `on_eventsource` — SSE event callback
- `on_authenticate_websocket` / `on_authenticate_sse` — upgrade guards
- `tls` / `tls_io_func` — optional TLS context
- `queue` — optional `fio_io_async_s` for off-thread response work
- `public_folder` — static file serving with `gz` pre-compressed support
HTTP handles (`fio_http_s`) are created per request and carry headers, body,
and state. The `fio_http_s` lifetime is managed by the library; callbacks
receive a pointer valid only for the duration of the call (or while an
explicit reference is held).
---
## Threading and Reactor Model
The IO reactor is **single-threaded per process**. All protocol callbacks
(`on_data`, `on_ready`, etc.) run in the reactor thread of the process that
owns the connection. There is no locking required for IO operations.
**Process model** (when `fio_io_start(workers)` is called with `workers > 0`):
```
master process
├── owns IPC listener socket
├── runs PRE_START state callbacks
├── forwards cluster / pub/sub to workers
└── worker process × N
├── each gets its own IO reactor loop
├── accepts connections (listeners are dup'd per worker)
└── communicates back via IPC socket
```
Workers are forked; the master sentinel thread monitors each child and
respawns crashed workers automatically (configurable via
`FIO_ASSERT_DEBUG` in debug builds to prevent respawn masking crashes).
**Deferred work** (`fio_io_defer`) posts tasks onto the reactor queue —
safe to call from any thread. The reactor processes the queue after each
poll cycle.
**Timers** (`fio_io_run_every`) schedule repeating or one-shot callbacks
aligned to the reactor tick (millisecond resolution). Timer precision depends
on the poll timeout and reactor load.
**Background threads** (`fio_io_async_s`): for blocking or CPU-intensive
work that must not stall the IO loop, attach a thread pool with
`fio_io_async_attach(q, thread_count)`. The pool starts and stops with the
reactor automatically.
**Connection lifecycle:**
```
fd created (accept / connect)
│
└─► fio_io_attach_fd → on_attach
│
├─ on_data (data available to read)
├─ on_ready (outgoing buffer drained)
├─ on_timeout (idle too long; default: close)
│
├─ on_shutdown (reactor shutting down)
└─ on_close (connection closed — cleanup here)
```
Outgoing writes are buffered. When the buffer exceeds
`FIO_IO_THROTTLE_LIMIT` (default 2 MiB), `on_data` events are suspended
until the buffer drains ("throttling"). The shutdown sequence gives
connections up to `FIO_IO_SHUTDOWN_TIMEOUT` (default 15 s) to flush before
force-closing.
**`fio_io_env_set` / `fio_io_env_get`**: attach named objects to the
connection's lifetime. The `on_close` callback fires automatically when the
connection closes, enabling RAII-style per-connection resource management.
---
## TLS Disambiguation
There are **two separate TLS documents** in this library:
| Document | What it covers |
|----------|----------------|
| [`./405 tls13.md`](./405 tls13.md) *(planned)* | IO-layer TLS integration — how TLS is plugged into the IO reactor via `fio_io_functions_s`. Use this when setting up TLS listeners or clients. |
| [`./190 tls13.md`](./190 tls13.md) | Standalone TLS 1.3 crypto library — key schedule, record layer, handshake state machines, alerts, KeyUpdate, certificate helpers. The raw machinery. |
If you are adding TLS to a server or client, start with the `405` docs.
If you are implementing a custom TLS backend or need direct access to the TLS 1.3 internals, start with `./190 tls13.md`.
---
## Documentation Index
| Header | Module | Doc |
|--------|--------|-----|
| `401 io api.h` | IO Reactor API | [./401 io api.md](./401 io api.md) |
| `402 io types.h` | IO Types (internal) | covered by IO docs |
| `403 io reactor.h` | IO Reactor cycle (internal) | covered by IO docs |
| `404 ipc.h` | IPC | [./404 ipc.md](./404 ipc.md) |
| `405 openssl.h` | OpenSSL TLS backend | [./405 openssl.md](./405 openssl.md) |
| `405 tls13.h` | Native TLS 1.3 IO backend | [./405 tls13.md](./405 tls13.md) |
| `420 pubsub.h` | Pub/Sub | [./420 pubsub.md](./420 pubsub.md) |
| `422 redis.h` | Redis engine | [./422 redis.md](./422 redis.md) |
| `004 http1 parser.h` | HTTP/1.1 parser | [./004 http1 parser.md](./004 http1 parser.md) |
| `004 websocket parser.h` | WebSocket parser | [./004 websocket parser.md](./004 websocket parser.md) |
| `432 http types.h` | HTTP types / handle (internal) | covered by HTTP docs |
| `439 http.h` | HTTP server / client | [./439 http.md](./439 http.md) |
# IO Reactor API (401 io api.h)
```c
#define FIO_IO
#include FIO_INCLUDE_FILE
```
`FIO_IO` adds the facil.io evented IO reactor, connection handles, protocol
callbacks, listener/client helpers, TLS transport hooks, per-connection
lifetime storage, timers, and optional async worker queues.
See [./400 io-overview.md](./400 io-overview.md) for the full IO / IPC /
PubSub / HTTP stack. TLS backends are covered by
[./405 openssl.md](./405 openssl.md) and [./405 tls13.md](./405 tls13.md).
Socket helpers are documented in [./004 sock.md](./004 sock.md), timers and
queues in [./102 queue.md](./102 queue.md), and lifecycle callbacks in
[./004 state callbacks.md](./004 state callbacks.md).
---
## Configuration Macros
Define before including the header to override defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_IO_BUFFER_PER_WRITE` | `65536U` | Stack buffer size used during write events. |
| `FIO_IO_THROTTLE_LIMIT` | `2097152U` | `on_data` is throttled while outgoing backlog is large. |
| `FIO_IO_TIMEOUT_MAX` | `300000` | Maximum and default connection timeout, in milliseconds. |
| `FIO_IO_SHUTDOWN_TIMEOUT` | `15000` | Hard timeout for the reactor shutdown loop, in milliseconds. |
| `FIO_IO_COUNT_STORAGE` | `1` in `DEBUG`, else `0` | Enables IO byte-count storage when compiled in. |
---
## Core Types
| Type | Role |
|------|------|
| `fio_io_s` | Opaque connection handle. Use it instead of raw file descriptors inside protocol callbacks. |
| `fio_io_protocol_s` | Shared protocol callback table for a family of connections. Usually static / global and zero-initialized. |
| `fio_io_functions_s` | Optional transport vtable used to override socket IO, mainly for TLS. |
| `fio_io_tls_s` | Reference-counted TLS settings object consumed by transport backends. |
| `fio_io_listener_s` | Listener handle returned by `fio_io_listen`. |
| `fio_pubsub_msg_s` | Message delivered to `on_pubsub` callbacks; defined by the Pub/Sub module. |
| `fio_io_async_s` | Async worker queue attached to the reactor lifecycle. |
---
## Reactor Lifecycle and State
### Start / stop
```c
void fio_io_start(int workers);
void fio_io_stop(void);
void fio_io_add_workers(int workers);
void fio_io_restart(int workers);
void fio_io_restart_on_signal(int signal);
size_t fio_io_shutdown_timeout(void);
size_t fio_io_shutdown_timeout_set(size_t milliseconds);
```
`fio_io_start(workers)` starts the reactor and blocks until shutdown. A positive
`workers` value forks worker processes. `fio_io_stop()` asks the reactor to
stop. `fio_io_add_workers` and `fio_io_restart` are cluster/process helpers;
`fio_io_restart_on_signal` binds hot restart to a signal.
`fio_io_shutdown_timeout_set` changes the hard shutdown grace period and returns
the value that will be used.
### State queries
```c
int fio_io_is_running(void);
int fio_io_is_master(void);
int fio_io_is_worker(void);
uint16_t fio_io_workers(int workers_requested);
int fio_io_pid(void);
int fio_io_root_pid(void);
int64_t fio_io_last_tick(void);
int64_t fio_io_last_tick_time(void);
```
`fio_io_last_tick()` is the cached millisecond value from the last reactor poll.
`fio_io_last_tick_time()` is the cached wall-clock millisecond timestamp,
useful for approximate log and HTTP date values.
---
## Protocols
```c
typedef struct fio_io_protocol_s fio_io_protocol_s;
struct fio_io_protocol_s {
struct { /* reserved; initialize to zero */ } reserved;
void (*on_attach)(fio_io_s *io);
void (*on_data)(fio_io_s *io);
void (*on_ready)(fio_io_s *io);
void (*on_shutdown)(fio_io_s *io);
void (*on_timeout)(fio_io_s *io);
void (*on_close)(void *iobuf, void *udata);
void (*on_pubsub)(fio_pubsub_msg_s *msg);
void (*on_user1)(fio_io_s *io, void *user_data);
void (*on_user2)(fio_io_s *io, void *user_data);
void (*on_user3)(fio_io_s *io, void *user_data);
void (*on_reserved)(fio_io_s *io, void *user_data);
fio_io_functions_s io_functions;
uint32_t timeout;
uint32_t buffer_size;
};
```
Protocol structs define connection behavior. They are normally static/global and
shared by many IO handles. Initialize the full struct to zero and never mutate
`reserved`.
Callback flow:
1. `on_attach` after an IO is attached to the protocol.
2. `on_data` when incoming data can be read.
3. `on_ready` after all pending writes drain.
4. `on_timeout` when the connection timeout is reached.
5. `on_shutdown` immediately before reactor shutdown closes the connection.
6. `on_close(iobuf, udata)` after the connection is closed.
All protocol callbacks return `void`. Use `fio_io_close`,
`fio_io_close_now`, or protocol state to control what happens next. Set
`on_timeout = fio_io_touch` for connections where idle timeout should be
ignored.
`timeout` is in milliseconds, capped by `FIO_IO_TIMEOUT_MAX`; `0` means the
maximum/default. `buffer_size` controls the per-connection protocol buffer
returned by `fio_io_buffer`.
### Iterating protocol IOs
```c
size_t fio_io_protocol_each(fio_io_protocol_s *protocol,
void (*task)(fio_io_s *, void *udata2),
void *udata2);
```
Runs `task` for each IO using `protocol`. Call only from the main IO thread;
use `fio_io_defer` when scheduling from another thread.
---
## Transport Functions / TLS Hooks
```c
typedef struct fio_io_functions_s fio_io_functions_s;
struct fio_io_functions_s {
void *(*build_context)(fio_io_tls_s *tls, uint8_t is_client);
void (*free_context)(void *context);
void (*start)(fio_io_s *io);
ssize_t (*read)(fio_socket_i fd, void *buf, size_t len, void *context);
ssize_t (*write)(fio_socket_i fd, const void *buf, size_t len, void *context);
int (*flush)(fio_socket_i fd, void *context);
void (*finish)(fio_socket_i fd, void *context);
void (*cleanup)(void *context);
};
```
The vtable lets a protocol replace plain socket IO with a transport layer such
as TLS. Functions receive the file descriptor but must not keep it or defer fd
operations; the `fio_io_s` handle is the long-lived identity.
Return conventions:
- `read` behaves like non-blocking `read(2)`.
- `write` returns plaintext bytes accepted (`N > 0`), `0` for zero-length
input, or `-1` with `errno` set. If data was transformed (for example,
encrypted), return success even if the underlying socket later blocks; use
`flush` for pending transformed bytes.
- `flush` returns `0` only when all internal output is empty. Non-zero (`N > 0`
or `-1`) means pending data remains and the reactor should keep watching for
writability.
- `finish` runs before closing after output is sent; `cleanup` releases the
per-connection transport context after close.
Set default TLS transport functions before the reactor starts:
```c
fio_io_functions_s fio_io_tls_default_functions(fio_io_functions_s *funcs);
```
Passing `NULL` returns the current default. Passing a pointer sets a new
default and returns the selected functions.
---
## Listening and Connecting
### `fio_io_listen`
```c
typedef struct fio_io_listen_args_s {
const char *url;
fio_io_protocol_s *protocol;
void *udata;
fio_io_tls_s *tls;
void (*on_start)(fio_io_protocol_s *protocol, void *udata);
void (*on_stop)(fio_io_protocol_s *protocol, void *udata);
fio_io_async_s *queue_for_accept;
uint8_t on_root;
uint8_t hide_from_log;
} fio_io_listen_args_s;
fio_io_listener_s *fio_io_listen(fio_io_listen_args_s args);
#define fio_io_listen(...) fio_io_listen((fio_io_listen_args_s){__VA_ARGS__})
```
Creates a network listener and returns a self-destructible listener handle, or
`NULL` on error. The default URL is `tcp://0.0.0.0:3000`.
Call it before `fio_io_start()` for normal server setup. The header notes that
this schedules a task and should not be called from `PRE_START` or `ON_START`
state callbacks.
TLS can be supplied directly with `.tls` or inferred from the URL query:
```c
fio_io_listen(.url = "0.0.0.0:3000/?tls", .protocol = &MY_PROTOCOL);
fio_io_listen(.url = "0.0.0.0:3000/?tls=./certs/", .protocol = &MY_PROTOCOL);
fio_io_listen(.url = "0.0.0.0:3000/?key=./key.pem&cert=./cert.pem",
.protocol = &MY_PROTOCOL);
```
`.tls` ownership is moved to the listener. If you need to share a TLS settings
object, duplicate it first with `fio_io_tls_dup` and free your own references
with `fio_io_tls_free`.
Listener helpers:
```c
void fio_io_listen_stop(fio_io_listener_s *l);
fio_io_protocol_s *fio_io_listener_protocol(fio_io_listener_s *l);
void *fio_io_listener_udata(fio_io_listener_s *l);
void *fio_io_listener_udata_set(fio_io_listener_s *l, void *new_udata);
fio_buf_info_s fio_io_listener_url(fio_io_listener_s *l);
int fio_io_listener_is_tls(fio_io_listener_s *l);
```
### `fio_io_connect`
```c
typedef struct {
const char *url;
fio_io_protocol_s *protocol;
void (*on_failed)(fio_io_protocol_s *protocol, void *udata);
void *udata;
fio_io_tls_s *tls;
uint32_t timeout;
} fio_io_connect_args_s;
fio_io_s *fio_io_connect(fio_io_connect_args_s args);
#define fio_io_connect(url_, ...) \
fio_io_connect((fio_io_connect_args_s){.url = url_, __VA_ARGS__})
```
Connects to `url` as a client and returns the IO handle or `NULL`. The URL may
contain TLS hints. `timeout` defaults to 30 seconds. `on_failed` is the cleanup
hook for failed connection attempts; established connections use the protocol
callbacks.
---
## IO Handles and Operations
```c
fio_io_s *fio_io_attach_fd(fio_socket_i fd,
fio_io_protocol_s *protocol,
void *udata,
void *tls);
fio_io_protocol_s *fio_io_protocol_set(fio_io_s *io,
fio_io_protocol_s *protocol);
fio_io_protocol_s *fio_io_protocol(fio_io_s *io);
void *fio_io_buffer(fio_io_s *io);
size_t fio_io_buffer_len(fio_io_s *io);
void *fio_io_udata_set(fio_io_s *io, void *udata);
void *fio_io_udata(fio_io_s *io);
void *fio_io_tls_set(fio_io_s *io, void *tls);
void *fio_io_tls(fio_io_s *io);
fio_socket_i fio_io_fd(fio_io_s *io);
void fio_io_touch(fio_io_s *io);
size_t fio_io_read(fio_io_s *io, void *buf, size_t len);
void fio_io_close(fio_io_s *io);
void fio_io_close_now(fio_io_s *io);
fio_io_s *fio_io_dup(fio_io_s *io);
void fio_io_free(fio_io_s *io);
void fio_io_suspend(fio_io_s *io);
void fio_io_unsuspend(fio_io_s *io);
int fio_io_is_suspended(fio_io_s *io);
int fio_io_is_open(fio_io_s *io);
size_t fio_io_backlog(fio_io_s *io);
void fio_io_noop(fio_io_s *io);
```
`fio_io_attach_fd` adopts a valid socket into the reactor. It returns `NULL` on
error. The returned pointer must not be used arbitrarily; IO handles are valid
inside proper callbacks and scheduled tasks. If code must keep a handle outside
that context, call `fio_io_dup` and later `fio_io_free`. These two functions are
thread-safe.
`fio_io_protocol_set` installs a new protocol. `NULL` is a valid "only-write"
protocol. The accessor may temporarily return the old protocol while the change
is being attached.
`fio_io_read` returns bytes read. `0` is not EOF by itself; it can mean no data
was available on the non-blocking socket. Use close callbacks for final cleanup.
`fio_io_close` closes after scheduled data is sent. `fio_io_close_now` closes as
soon as possible. `fio_io_is_open` returns 1 only while open and not marked for
closure: it returns 0 immediately after `fio_io_close`, even while previously
scheduled output is draining. `fio_io_suspend` / `fio_io_unsuspend` control future
`on_data` delivery, and `fio_io_backlog` reports the approximate outgoing byte
count.
### Writing
```c
typedef struct {
void *buf;
intptr_t fd;
size_t len;
size_t offset;
void (*dealloc)(void *);
uint8_t copy;
} fio_io_write_args_s;
void fio_io_write2(fio_io_s *io, fio_io_write_args_s args);
#define fio_io_write2(io, ...) \
fio_io_write2(io, (fio_io_write_args_s){__VA_ARGS__})
#define fio_io_write(io, buf_, len_) \
fio_io_write2(io, .buf = (buf_), .len = (len_), .copy = 1)
#define fio_io_sendfile(io, source_fd, offset_, bytes) \
fio_io_write2((io), .fd = (source_fd), .offset = (size_t)(offset_), .len = (bytes))
```
`fio_io_write2` schedules buffered output. If `.buf` is used and `.copy` is
non-zero, the data is copied immediately. If `.copy == 0` and `.dealloc` is set,
the IO layer takes ownership of the buffer and calls `dealloc` later. If
`.dealloc == NULL`, the buffer is not freed by the IO layer.
For file output, pass `.fd`; `.len == 0` means send the whole file. The
`fio_io_sendfile` helper closes `source_fd` after sending or on error.
---
## Scheduling Tasks and Timers
```c
void fio_io_defer(void (*task)(void *, void *), void *udata1, void *udata2);
void fio_io_run_every(fio_timer_schedule_args_s args);
#define fio_io_run_every(...) \
fio_io_run_every((fio_timer_schedule_args_s){__VA_ARGS__})
fio_queue_s *fio_io_queue(void);
```
`fio_io_defer` schedules a task on the IO reactor queue and is thread-safe.
Use it to move work from other threads back to the IO thread.
`fio_io_run_every` schedules a timer. It uses `fio_timer_schedule_args_s` from
the queue/timer API:
- `fn` returns non-zero to stop the timer.
- `udata1` and `udata2` are passed to callbacks.
- `on_stop` runs when the timer ends.
- `every` is the interval in milliseconds.
- `repetitions` is the number of runs; `-1` means indefinitely.
`fio_io_queue()` returns the reactor queue.
---
## Connection Environment
The IO environment links named objects to a connection lifetime. When the IO is
closed, stored `on_close` callbacks run automatically.
```c
typedef struct {
intptr_t type;
fio_buf_info_s name;
void *udata;
void (*on_close)(void *data);
uint8_t const_name;
} fio_io_env_set_args_s;
typedef struct {
intptr_t type;
fio_buf_info_s name;
} fio_io_env_get_args_s;
void *fio_io_env_get(fio_io_s *io, fio_io_env_get_args_s args);
void fio_io_env_set(fio_io_s *io, fio_io_env_set_args_s args);
int fio_io_env_unset(fio_io_s *io, fio_io_env_get_args_s args);
int fio_io_env_remove(fio_io_s *io, fio_io_env_get_args_s args);
#define fio_io_env_get(io, ...) \
fio_io_env_get(io, (fio_io_env_get_args_s){__VA_ARGS__})
#define fio_io_env_set(io, ...) \
fio_io_env_set(io, (fio_io_env_set_args_s){__VA_ARGS__})
#define fio_io_env_unset(io, ...) \
fio_io_env_unset(io, (fio_io_env_get_args_s){__VA_ARGS__})
#define fio_io_env_remove(io, ...) \
fio_io_env_remove(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
`type` and `name` together identify an entry. Negative `type` values are
reserved. If `const_name` is set, the name string must outlive the environment.
If `io == NULL`, entries are stored in the global environment; their `on_close`
callbacks run when the process exits. `unset` detaches without calling
`on_close`; `remove` detaches and calls `on_close` as if the connection closed.
---
## TLS Settings Helpers
```c
fio_io_tls_s *fio_io_tls_new(void);
fio_io_tls_s *fio_io_tls_from_url(fio_io_tls_s *target_or_null, fio_url_s url);
fio_io_tls_s *fio_io_tls_dup(fio_io_tls_s *tls);
void fio_io_tls_free(fio_io_tls_s *tls);
fio_io_tls_s *fio_io_tls_cert_add(fio_io_tls_s *tls,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
fio_io_tls_s *fio_io_tls_alpn_add(fio_io_tls_s *tls,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int fio_io_tls_alpn_select(fio_io_tls_s *tls,
const char *protocol_name,
size_t name_length,
fio_io_s *io);
fio_io_tls_s *fio_io_tls_trust_add(fio_io_tls_s *tls,
const char *public_cert_file);
uintptr_t fio_io_tls_cert_count(fio_io_tls_s *tls);
uintptr_t fio_io_tls_alpn_count(fio_io_tls_s *tls);
uintptr_t fio_io_tls_trust_count(fio_io_tls_s *tls);
```
`fio_io_tls_s` stores backend-neutral TLS instructions. Add certificates for
SNI, ALPN protocol callbacks, and trusted certificates / CA stores. Passing
`NULL` certificate paths can request a backend-provided self-signed certificate;
passing `NULL` to `fio_io_tls_trust_add` asks the backend to use system trust.
Backend behavior is documented in the TLS backend docs.
`fio_io_tls_alpn_add` silently ignores a `NULL` protocol name and replaces a
`NULL` callback with a no-op. The first ALPN protocol added is the default.
### Iterating TLS settings
```c
typedef struct fio_io_tls_each_s {
fio_io_tls_s *tls;
void *udata;
void *udata2;
int (*each_cert)(struct fio_io_tls_each_s *,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
int (*each_alpn)(struct fio_io_tls_each_s *,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int (*each_trust)(struct fio_io_tls_each_s *, const char *public_cert_file);
} fio_io_tls_each_s;
int fio_io_tls_each(fio_io_tls_each_s args);
#define fio_io_tls_each(tls_, ...) \
fio_io_tls_each(((fio_io_tls_each_s){.tls = tls_, __VA_ARGS__}))
```
Transport backends use `fio_io_tls_each` to consume the stored instructions.
Iterator callbacks return `int`; check the backend using them for any additional
meaning assigned to non-zero return values.
### Peer certificate inspection (mTLS)
```c
int fio_io_peer_info_next(fio_io_s *io, fio_x509_cert_s *dest);
```
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: a server context verifies (and requires) client certificates,
a client context verifies the server certificate. An empty trust list means
no peer verification; passing `NULL` to `fio_io_tls_trust_add` selects the
system trust store.
Once the handshake completed, iterate the peer's certificate chain (leaf
first) to implement application level client certificate authentication and
authorization:
```c
fio_x509_cert_s cert = {0}; /* zeroed = new loop */
while (fio_io_peer_info_next(io, &cert) == 0) {
if (!cert.verified)
continue; /* chain failed TLS-level verification (or was skipped) */
/* authorize by identity, e.g.: cert.cn,
* cert.subject, or pin cert.fingerprint (32-byte SHA-256) */
}
```
The iterator is stateless — the position is identified from `dest` alone: a
zeroed `dest` (`der.buf == NULL`) starts a new loop; otherwise iteration
continues at `dest->chain_index + 1`. To restart a loop, zero the struct.
Multiple loops may iterate the same connection concurrently.
Iteration is capped at 128 certificates (`chain_index` 0..127) as a
deep-nesting / DoS guard — longer chains end the loop with -1.
Passing a `NULL` `dest` returns -1. The function returns
-1 when the iteration is done or when peer information is unavailable (no
TLS, handshake incomplete, peer sent no certificate, or the X509 module
missing).
Each call parses the next certificate into a `fio_x509_cert_s` (defined in
the X509 module, `156 x509.h`). All certificate fields point directly into
memory owned by the TLS backend — nothing is copied or allocated. The views
stay valid until the next `fio_io_peer_info_next` call (on ANY connection)
or connection close, whichever comes first; copy anything that must outlive
the loop.
The `verified` flag reflects the TLS backend's verification of the whole
chain and is identical for every certificate in the chain. Connections that
skipped verification (empty trust list) report `verified == 0`.
---
## Async Worker Queues
```c
typedef struct fio_io_async_s fio_io_async_s;
#define FIO_IO_ASYN_INIT ((fio_io_async_s){0})
fio_queue_s *fio_io_async_queue(fio_io_async_s *q);
void fio_io_async_attach(fio_io_async_s *q, uint32_t threads);
#define fio_io_async(q_, ...) fio_queue_push((q_)->q, __VA_ARGS__)
void fio_io_async_every(fio_io_async_s *q, fio_timer_schedule_args_s args);
#define fio_io_async_every(async, ...) \
fio_io_async_every(async, (fio_timer_schedule_args_s){__VA_ARGS__})
```
Async queues are for non-IO work that must not block the reactor. Allocate the
`fio_io_async_s` object with static or otherwise reactor-long lifetime and
initialize it with `FIO_IO_ASYN_INIT` or zeroes before attaching it.
```c
static fio_io_async_s SLOW_TASKS = FIO_IO_ASYN_INIT;
int main(void) {
fio_io_async_attach(&SLOW_TASKS, 32);
fio_io_start(0);
}
```
The queue starts and stops with the IO reactor. Use `fio_io_async_queue` when a
raw `fio_queue_s *` is needed, `fio_io_async` to push tasks, and
`fio_io_async_every` for timers on that async queue.
---
## Minimal Echo Server
```c
#define FIO_IO
#include "fio-stl/include.h"
static void echo_on_data(fio_io_s *io) {
char buf[4096];
size_t len;
while ((len = fio_io_read(io, buf, sizeof(buf))))
fio_io_write(io, buf, len); /* copies stack buffer */
}
static void echo_on_timeout(fio_io_s *io) { fio_io_close(io); }
static fio_io_protocol_s ECHO = {
.on_data = echo_on_data,
.on_timeout = echo_on_timeout,
.timeout = 30000,
};
int main(void) {
fio_io_listen(.url = "tcp://0.0.0.0:3000", .protocol = &ECHO);
fio_io_start(0);
}
```
---
## Common Lifetime Rules
- Treat `fio_io_s *` as callback/task scoped unless you hold a reference with
`fio_io_dup`; release it with `fio_io_free`.
- Do not keep raw file descriptors received by transport callbacks.
- Protocol objects should be zero-initialized and live at least as long as any
IO that uses them.
- Listener `.tls` is moved to the listener; duplicate TLS settings before
sharing them elsewhere.
- `fio_io_write` copies the buffer; `fio_io_write2` can copy, borrow, or take
ownership depending on `.copy` and `.dealloc`.
- Environment `on_close` callbacks are the preferred cleanup point for objects
tied to a connection lifetime.
# IPC — Inter-Process Communication (404 ipc.h)
```c
#define FIO_IPC
#include FIO_INCLUDE_FILE
```
IPC is the messaging backbone of the facil.io worker/master model. Workers
fork from a master process. The master keeps listeners, manages state, and
handles anything that needs a single authoritative view of the world. Workers
handle client connections. IPC bridges the two: a worker sends a message to
the master (or to every process in the cluster), the master executes it and
optionally streams replies back.
All messages are encrypted in transit with ChaCha20-Poly1305 AEAD, keyed from
the process secret. Local IPC runs over a Unix socket (or auto-generated TCP
socket). Multi-machine RPC extends the same protocol over TCP with optional
UDP peer discovery.
See [./400 io-overview.md](./400 io-overview.md) for where IPC sits in the
full IO stack.
---
## Worker/Master Model
```
master process
├── owns IPC listener socket
├── executes messages sent by workers
├── forwards cluster/broadcast messages to other machines
└── worker process × N
├── each has its own IO reactor loop
├── connects back to master via IPC socket on startup
└── sends fio_ipc_* messages to reach master or peers
```
The master is always the hub. Workers can only talk *through* the master —
there is no direct worker-to-worker channel. On shutdown the master sends a
local broadcast instructing all workers to stop.
---
## Configuration Macros
Define before including the header to override defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_IPC_URL_MAX_LENGTH` | `1024` | Max IPC socket URL length |
| `FIO_IPC_MAX_LENGTH` | `128 MiB` | Max message payload size |
---
## Types
### `fio_ipc_s` — the message
```c
typedef struct fio_ipc_s {
fio_io_s *from; /* originating IO (set by receiver; not transmitted) */
/* -- wire format starts here -- */
uint32_t len; /* data[] byte count (AAD - authenticated) */
uint16_t flags; /* user-settable flags (AAD - authenticated) */
uint16_t routing_flags; /* internal routing (AAD - authenticated) */
uint64_t timestamp; /* millisecond tick — part of nonce (unencrypted) */
uint64_t id; /* random 64-bit value — part of nonce (unencrypted) */
union {
void (*call)(struct fio_ipc_s *); /* function to call (local IPC; encrypted) */
uint32_t opcode; /* registered op-code (cluster RPC; encrypted)*/
};
void (*on_reply)(struct fio_ipc_s *); /* run on caller when reply arrives (encrypted) */
void (*on_done)(struct fio_ipc_s *); /* run on caller when last reply arrives (encrypted) */
void *udata; /* caller-side opaque pointer (encrypted) */
char data[]; /* payload + 16-byte Poly1305 MAC at tail (encrypted) */
} fio_ipc_s;
```
Messages are reference-counted. Everything from `call` onward (including the
payload) is encrypted on the wire. `from`, `timestamp`, and `id` are unencrypted
and unauthenticated (they form the nonce). `len`, `flags`, and `routing_flags`
are unencrypted but authenticated via AEAD.
### `fio_ipc_args_s` — construction arguments
```c
typedef struct {
void (*call)(fio_ipc_s *); /* function to run on target (required for local IPC) */
void (*on_reply)(fio_ipc_s *); /* called on caller when a reply arrives */
void (*on_done)(fio_ipc_s *); /* called on caller when final reply arrives */
fio_io_s *exclude; /* IO to exclude from delivery (or FIO_IPC_EXCLUDE_SELF) */
uint64_t timestamp; /* force timestamp; 0 = use reactor tick */
uint64_t id; /* force message ID; 0 = random */
uint32_t opcode; /* registered op-code; replaces call if non-zero */
uint16_t flags; /* user-settable flags */
bool cluster; /* deliver to remote machines in cluster */
bool workers; /* deliver to master + all local workers */
void *udata; /* opaque pointer for reply callbacks */
fio_buf_info_s *data; /* payload (use FIO_IPC_DATA macro) */
} fio_ipc_args_s;
```
### `fio_ipc_opcode_s` — op-code registration
```c
typedef struct fio_ipc_opcode_s {
uint32_t opcode; /* unique non-zero value */
void (*call)(struct fio_ipc_s *); /* handler */
void (*on_reply)(struct fio_ipc_s *); /* reply callback */
void (*on_done)(struct fio_ipc_s *); /* final reply callback */
void *udata; /* provided to handler via ipc->udata */
} fio_ipc_opcode_s;
```
### `fio_ipc_reply_args_s` — reply arguments
```c
typedef struct {
fio_ipc_s *ipc; /* original request (required) */
fio_buf_info_s *data; /* reply payload */
uint64_t timestamp; /* 0 = current tick */
uint64_t id; /* 0 = use original request ID */
uint16_t flags; /* 0 = use original request flags */
uint8_t done; /* set to 1 on last reply (triggers on_done on caller) */
uint8_t flags_set; /* set to 1 to allow flags=0 override */
} fio_ipc_reply_args_s;
```
---
## Helper Macro: `FIO_IPC_DATA`
```c
#define FIO_IPC_DATA(...) \
(fio_buf_info_s[]){ __VA_ARGS__, { .len = ((size_t)-1) } }
```
Composes multiple buffers into a single message without intermediate
allocation. Iteration stops at the first entry with `len == (size_t)-1`.
```c
uint32_t seq = 42;
const char *text = "hello";
fio_ipc_call(.call = my_handler,
.data = FIO_IPC_DATA(
FIO_BUF_INFO2(&seq, sizeof(seq)),
FIO_BUF_INFO1((char *)text)));
/* seq and text are copied immediately — stack safe */
```
---
## Delivery Macros
All four macros call `fio_ipc_new(...)` then `fio_ipc_send(...)`. They accept
named fields from `fio_ipc_args_s`.
| Macro | Delivered to |
|-------|-------------|
| `fio_ipc_call(...)` | master only |
| `fio_ipc_local(...)` | master + all local workers |
| `fio_ipc_cluster(...)` | master process on every machine (local and remote) |
| `fio_ipc_broadcast(...)` | master + workers on every machine (local and remote) |
```c
/* worker → master */
#define fio_ipc_call(...) fio_ipc_send(fio_ipc_new(__VA_ARGS__))
/* all local processes */
#define fio_ipc_local(...) fio_ipc_send(fio_ipc_new(.workers = 1, __VA_ARGS__))
/* remote masters only */
#define fio_ipc_cluster(...) fio_ipc_send(fio_ipc_new(.cluster = 1, __VA_ARGS__))
/* everywhere */
#define fio_ipc_broadcast(...) fio_ipc_send(fio_ipc_new(.workers = 1, .cluster = 1, __VA_ARGS__))
```
`fio_ipc_cluster` and `fio_ipc_broadcast` require an `opcode` (not a function
pointer) because function pointers are meaningless across machine boundaries.
Use `.exclude = FIO_IPC_EXCLUDE_SELF` to skip the calling process when
broadcasting locally.
**`FIO_IPC_EXCLUDE_SELF`**
```c
#define FIO_IPC_EXCLUDE_SELF ((fio_io_s *)((char *)-1LL))
```
Pass as `.exclude` in `fio_ipc_args_s`, or set `ipc->from = FIO_IPC_EXCLUDE_SELF`
before calling `fio_ipc_send`.
---
## Quick Examples
### Worker calls master, master replies
```c
/* runs on master */
void on_master(fio_ipc_s *msg) {
printf("master got: %.*s\n", (int)msg->len, msg->data);
fio_ipc_reply(msg,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"pong")),
.done = 1);
}
/* runs on calling worker when reply arrives */
void on_reply(fio_ipc_s *msg) {
printf("worker got reply: %.*s\n", (int)msg->len, msg->data);
}
/* somewhere in a worker callback */
fio_ipc_call(.call = on_master,
.on_done = on_reply,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"ping")));
```
### Local broadcast (skip self)
```c
void on_notify(fio_ipc_s *msg) {
printf("[%d] notified: %.*s\n", fio_io_pid(), (int)msg->len, msg->data);
}
fio_ipc_local(.call = on_notify,
.exclude = FIO_IPC_EXCLUDE_SELF,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"config-reload")));
```
### Cluster-wide op-code broadcast
```c
#define OP_CACHE_BUST 1
void on_cache_bust(fio_ipc_s *msg) {
cache_invalidate(msg->data, msg->len);
}
/* register before fio_io_start */
fio_ipc_opcode_register(.opcode = OP_CACHE_BUST, .call = on_cache_bust);
fio_ipc_cluster_listen(9000); /* requires SECRET env var */
fio_io_start(4);
/* later, from any process */
fio_ipc_broadcast(.opcode = OP_CACHE_BUST,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"key123")));
```
---
## Reply: `fio_ipc_reply`
```c
SFUNC void fio_ipc_reply(fio_ipc_reply_args_s args);
/* shadowing macro — first arg is the original request */
#define fio_ipc_reply(r, ...) \
fio_ipc_reply((fio_ipc_reply_args_s){ .ipc = (r), __VA_ARGS__ })
```
Send one or more replies from the master back to the caller. Set `.done = 1`
on the final reply to trigger the caller's `on_done` callback. Replies inherit
`call`, `on_reply`, `on_done`, and `udata` from the original request.
Streaming replies:
```c
void stream_handler(fio_ipc_s *msg) {
for (int i = 0; i < 5; ++i) {
char chunk[32];
int n = snprintf(chunk, sizeof(chunk), "chunk%d", i);
fio_ipc_reply(msg,
.data = FIO_IPC_DATA(FIO_BUF_INFO2(chunk, (size_t)n)),
.done = (i == 4));
}
}
```
---
## Op-Code Registration
Op-codes let you perform cluster-wide RPC without transmitting function
pointers (which differ per binary). Register before `fio_io_start`.
```c
SFUNC int fio_ipc_opcode_register(fio_ipc_opcode_s opcode);
/* named-arg macro */
#define fio_ipc_opcode_register(...) \
fio_ipc_opcode_register((fio_ipc_opcode_s){ __VA_ARGS__ })
/** Returns registered op-code or NULL. */
SFUNC const fio_ipc_opcode_s *fio_ipc_opcode(uint32_t opcode);
```
- Op-codes must be non-zero `uint32_t` values.
- Values `>= 0xFF000000` are reserved for internal use.
- Pass `call = NULL` to unregister.
- Must be called before `fio_io_start()` (not thread-safe after that).
- Returns 0 on success, -1 on error.
```c
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = on_sync, .udata = ctx);
/* look up */
const fio_ipc_opcode_s *op = fio_ipc_opcode(OP_SYNC);
/* remove */
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = NULL);
```
---
## IPC URL
The IPC socket URL is auto-generated at startup (`unix://fio_tmp_.sock`
in `$TMPDIR`). Override before `fio_io_start` on the master:
```c
/** Returns current IPC socket URL. */
SFUNC const char *fio_ipc_url(void);
/**
* Sets IPC socket URL. Master only, before IO reactor starts.
* Pass NULL to regenerate. 'X' / '#' characters in the URL are replaced with
* random hex digits. Returns 0 on success, -1 on error.
*/
SFUNC int fio_ipc_url_set(const char *url);
```
```c
fio_ipc_url_set("unix:///var/run/myapp-XXXX.sock"); /* X replaced randomly */
fio_ipc_url_set(NULL); /* auto-generate */
printf("%s\n", fio_ipc_url());
```
---
## Multi-Machine Cluster (RPC)
```c
/**
* Listen for cluster peers on port. Auto-discovers peers via UDP broadcast.
* Requires a shared SECRET environment variable (non-random secret).
* Returns listener handle or NULL if disabled.
*/
SFUNC fio_io_listener_s *fio_ipc_cluster_listen(uint16_t port);
/** Manually connect to a cluster peer (usually unnecessary). */
SFUNC void fio_ipc_cluster_connect(const char *url);
/** Returns last port passed to fio_ipc_cluster_listen / connect, or 0. */
SFUNC uint16_t fio_ipc_cluster_port(void);
```
- Call `fio_ipc_cluster_listen` from the master before or after
`fio_io_start`. It opens a TCP listener and a UDP broadcast socket on the
same port for automatic peer discovery.
- Uses the environment's shared secret for encryption (ChaCha20-Poly1305,
no forward secrecy). Set `SECRET=` across all instances.
- All instances receive all `cluster` / `broadcast` messages.
- `fio_ipc_cluster_connect` lets you manually reach peers on other subnets
where UDP broadcast doesn't reach.
```c
/* Typical setup */
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = on_sync);
fio_ipc_cluster_listen(9000); /* returns NULL/no-op if SECRET is unset or random */
fio_io_start(4);
```
---
## Core Lifetime API
These are used internally by the delivery macros and are available for
advanced use (e.g., constructing messages for local execution, or async
message handling).
```c
/** Create a message without sending it. */
SFUNC fio_ipc_s *fio_ipc_new(fio_ipc_args_s args);
#define fio_ipc_new(...) fio_ipc_new((fio_ipc_args_s){ __VA_ARGS__ })
/** Encrypt, route, and free the message. Takes ownership. */
SFUNC void fio_ipc_send(fio_ipc_s *ipc);
/** Encrypt and send directly to a specific IO. Takes ownership. */
SFUNC void fio_ipc_send_to(fio_io_s *to, fio_ipc_s *ipc);
/** Increment reference count. */
SFUNC fio_ipc_s *fio_ipc_dup(fio_ipc_s *msg);
/** Decrement reference count; destroy when zero. */
SFUNC void fio_ipc_free(fio_ipc_s *msg);
/** Release the msg->from IO reference (call if storing msg beyond callback). */
SFUNC void fio_ipc_detach(fio_ipc_s *msg);
/** Set a one-shot callback invoked when sending is complete.
The callback receives ownership of the decrypted message and must free it. */
SFUNC void fio_ipc_after_send(fio_ipc_s *ipc,
void (*fn)(fio_ipc_s *, void *),
void *udata);
```
Typical async pattern:
```c
void my_handler(fio_ipc_s *msg) {
fio_ipc_s *ref = fio_ipc_dup(msg); /* keep alive past callback return */
fio_ipc_detach(ref); /* release the from-IO ref if unneeded */
do_async_work(ref); /* stored elsewhere */
}
void async_done(fio_ipc_s *msg) {
process(msg->data, msg->len);
fio_ipc_free(msg);
}
```
---
## Encryption
```c
/** Encrypt a message in-place (idempotent). Called automatically by fio_ipc_send. */
SFUNC void fio_ipc_encrypt(fio_ipc_s *m);
/** Decrypt a message in-place. Returns 0 on success, non-zero on MAC failure. */
FIO_IFUNC int fio_ipc_decrypt(fio_ipc_s *m);
```
ChaCha20-Poly1305 AEAD. Key derived from the shared process secret plus the
message's `id` and `timestamp` fields (which form the nonce). `len`, `flags`,
and `routing_flags` are authenticated but unencrypted. Everything else
(`call`/`opcode`, `on_reply`, `on_done`, `udata`, `data`) is encrypted.
Decryption failure logs a security warning and the connection is closed.
Only processes sharing the same secret can communicate.
---
## Routing Flags
`routing_flags` is internal — do not edit manually. Available for inspection:
```c
#define FIO_IPC_FLAG_ENCRYPTED ((uint16_t)1 << 0) /* message is encrypted */
#define FIO_IPC_FLAG_DONE ((uint16_t)1 << 1) /* final reply */
#define FIO_IPC_FLAG_OPCODE ((uint16_t)1 << 2) /* route via op-code */
#define FIO_IPC_FLAG_WORKERS ((uint16_t)1 << 3) /* deliver to workers too */
#define FIO_IPC_FLAG_CLUSTER ((uint16_t)1 << 4) /* deliver to remote machines */
#define FIO_IPC_FLAG_REPLY ((uint16_t)1 << 5) /* this is a reply */
#define FIO_IPC_FLAG_PING ((uint16_t)1 << 6) /* internal keepalive */
/** Test a flag: returns flag value if set, 0 otherwise. */
#define FIO_IPC_FLAG_TEST(msg, flag) (((msg)->routing_flags & (flag)) == (flag))
```
---
## Keepalive
IPC and cluster connections use an automatic ping/pong protocol. On the first
timeout the system sends a ping; if no pong arrives before the next timeout
the connection is closed. Ping frames are never delivered to user callbacks.
Default timeouts (set in the protocol initializer):
| Connection type | Timeout |
|-----------------|---------|
| Local IPC (master ↔ worker) | ~6 minutes |
| Cluster RPC (machine ↔ machine) | ~55 seconds |
---
## Memory Management
- Messages are reference-counted (`fio_ipc_dup` / `fio_ipc_free`).
- Callbacks receive a message pointer valid for the duration of the callback.
The system frees it automatically after the callback returns.
- Call `fio_ipc_dup` to keep a message alive beyond the callback, then
`fio_ipc_free` when done.
- Data passed via `FIO_IPC_DATA` is copied into the message immediately —
source buffers may be stack-allocated or freed right after the call.
---
## Thread Safety
- `fio_ipc_call`, `fio_ipc_local`, `fio_ipc_cluster`, `fio_ipc_broadcast`,
and `fio_ipc_reply` are safe to call from any thread.
- `fio_ipc_dup` / `fio_ipc_free` are thread-safe.
- `fio_ipc_opcode_register` is **not** thread-safe after `fio_io_start`.
- `fio_ipc_url_set` must be called on the master before `fio_io_start`.
# OpenSSL TLS Backend
```c
#define FIO_IO
#include "fio-stl/include.h"
```
OpenSSL 3.x IO-layer TLS backend for the facil.io reactor. When OpenSSL is
available at build time this module auto-registers itself as the default TLS
transport. You don't have to call anything — just define `FIO_IO` and link
against OpenSSL 3.x.
For IO-layer TLS context overview see [./400 io-overview.md](./400 io-overview.md).
For the sibling native TLS 1.3 backend see [./405 tls13.md](./405 tls13.md) *(planned)*.
For standalone TLS 1.3 crypto (key schedule, record layer) see [./190 tls13.md](./190 tls13.md).
---
## Activation
The module compiles automatically when all of the following are true:
| Condition | Notes |
|-----------|-------|
| `FIO_IO` is defined | pulls in the IO reactor |
| `FIO_NO_TLS` is **not** defined | opt-out guard |
| OpenSSL 3.x headers are present | `HAVE_OPENSSL` or `__has_include("openssl/ssl.h")` |
If OpenSSL headers are found but the version is older than 3.x
(`OPENSSL_VERSION_MAJOR < 3`), the module emits a compiler warning and
makes `fio_openssl_io_functions()` return `fio_io_tls_default_functions(NULL)`
(the current default, usually a no-op). Everything compiles; TLS simply does nothing.
A module-level constructor runs before `main()`:
- Initialises the custom BIO methods.
- Calls `fio_io_tls_default_functions(&openssl_io_funcs)` so every subsequent
`fio_io_listen` / `fio_io_connect` call with a `tls` argument uses OpenSSL.
- Registers a `SIGPIPE` monitor (OpenSSL can trigger SIGPIPE on broken sockets).
- Registers a `FIO_CALL_AT_EXIT` cleanup for the custom BIO method objects.
---
## Public API
### `fio_openssl_io_functions`
```c
fio_io_functions_s fio_openssl_io_functions(void);
```
Returns the `fio_io_functions_s` vtable that wires OpenSSL into the IO reactor:
| Field | Role |
|-------|------|
| `build_context` | Converts `fio_io_tls_s` into an `SSL_CTX` wrapper |
| `free_context` | Deferred free of `SSL_CTX` and the `fio_io_tls_s` reference |
| `start` | Per-connection: `SSL_new`, custom BIO setup, initial handshake |
| `read` | Non-blocking decrypt: advances handshake if needed, then `SSL_read_ex` |
| `write` | Non-blocking encrypt: `SSL_write_ex` → encrypt → socket |
| `flush` | Sends any pending encrypted bytes from the internal buffer |
| `finish` | Sends TLS `close_notify` before the TCP close |
| `cleanup` | Frees the per-connection `SSL` object |
Normally you never call this directly — the constructor handles registration.
Use it explicitly only when overriding the default or building a custom setup:
```c
/* Override: switch back to OpenSSL after something else changed the default */
fio_io_functions_s openssl_funcs = fio_openssl_io_functions();
fio_io_tls_default_functions(&openssl_funcs);
```
Or to set it on a specific protocol without touching the global default
(the `io_functions` field lives on `fio_io_protocol_s`):
```c
fio_io_functions_s openssl_funcs = fio_openssl_io_functions();
MY_PROTOCOL.io_functions = openssl_funcs; /* per-protocol override */
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &MY_PROTOCOL,
.tls = tls);
```
---
## Configuring TLS — `fio_io_tls_s`
TLS parameters are held in a `fio_io_tls_s` object defined in `401 io api.h`.
Build one before calling `fio_io_listen` or `fio_io_connect`:
```c
/* Allocate (reference counted) */
fio_io_tls_s *tls = fio_io_tls_new();
/* Certificate — PEM files */
fio_io_tls_cert_add(tls,
"www.example.com", /* server_name (SNI) */
"cert.pem", /* public certificate or chain */
"key.pem", /* private key */
NULL); /* PEM password, or NULL */
/* ALPN protocol negotiation */
fio_io_tls_alpn_add(tls, "h2", on_http2_selected);
fio_io_tls_alpn_add(tls, "http/1.1", on_http1_selected);
/* Peer certificate verification */
fio_io_tls_trust_add(tls, NULL); /* use system trust store */
fio_io_tls_trust_add(tls, "ca.pem"); /* or a specific CA bundle */
/* Listen */
fio_io_listen(.url = "0.0.0.0:443",
.protocol = &MY_PROTOCOL,
.tls = tls);
fio_io_tls_free(tls); /* release your reference; the listener holds its own */
fio_io_start(0);
```
`fio_io_tls_s` is reference-counted (`fio_io_tls_dup` / `fio_io_tls_free`).
The backend duplicates the reference during `build_context`; freeing yours
after `fio_io_listen` is always safe.
### URL shorthand
The URL query string can also configure TLS without building the object
manually:
```c
fio_io_listen(.url = "0.0.0.0:443/?tls=./certs/", .protocol = &MY_PROTOCOL);
```
See `fio_io_tls_from_url` in `401 io api.h` for the query syntax.
---
## Certificates
### Loading from PEM files
When `public_cert_file` and `private_key_file` are both provided to
`fio_io_tls_cert_add`, the backend:
1. Sets a PEM password callback if `pk_password` is non-`NULL`.
2. Loads the certificate chain via `SSL_CTX_use_certificate_chain_file`.
3. Loads the private key via `SSL_CTX_use_PrivateKey_file` (PEM format).
4. Verifies key-cert consistency with `SSL_CTX_check_private_key`.
Errors are logged with `FIO_LOG_ERROR` and the context build fails.
### Self-signed fallback
When a **server** has no certificates configured (or `fio_io_tls_cert_add` is
called with both file arguments as `NULL`), the backend generates a self-signed
ECDSA P-256 certificate on the fly:
| Property | Value |
|----------|-------|
| Key algorithm | ECDSA P-256 (128-bit security ≈ RSA-3072) |
| Key generation time | ~10 ms |
| Signature | SHA-256 |
| Validity | 180 days |
| Serial number | 128-bit cryptographically random |
| X.509 extensions | Basic Constraints (`CA:FALSE`), Key Usage, Extended Key Usage (`serverAuth`), SAN |
The private key is generated once per process (thread-safe, lock-protected) and
freed at exit via a `FIO_CALL_AT_EXIT` callback.
Self-signed certificates are fine for development. Browsers will warn. Use a
CA-issued certificate (e.g. Let's Encrypt) in production.
---
## ALPN
Register protocols with `fio_io_tls_alpn_add` before listen/connect. The first
registered protocol is the preferred default.
When a client sends an ALPN extension, the backend walks the offered names in
order and picks the first that matches a registered protocol. On match it calls
`fio_io_tls_alpn_select`, which fires the `on_selected` callback for that
connection. If nothing matches, the handshake fails with a fatal alert.
Protocol names must be 1–255 bytes. The internal wire-format list is capped at
1 023 bytes total; more protocols than that will log an overflow error.
---
## Trust and Peer Verification
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: a client context verifies the server certificate, a server
context requests, requires, and verifies the client certificate (mutual TLS).
An empty trust list means **no peer verification**.
| Scenario | Behaviour |
|----------|-----------|
| `fio_io_tls_trust_add` called (any argument) | `SSL_VERIFY_PEER` enabled |
| `NULL` passed to `fio_io_tls_trust_add` | system trust store is loaded (`X509_STORE_set_default_paths`) |
| Trust configured, server mode | `SSL_VERIFY_PEER \| SSL_VERIFY_FAIL_IF_NO_PEER_CERT` (client certificate required) |
| No trust certs added, client mode | `SSL_VERIFY_NONE` + `FIO_LOG_SECURITY` warning |
| No trust certs added, server mode | `SSL_VERIFY_NONE` (no client certificate requested) |
After the handshake, inspect the peer's certificate chain with
`fio_io_peer_info_next(io, &info)` (see the IO API docs). Each call
re-encodes the next chain certificate into a fixed per-connection staging
buffer (no allocation) and parses it into a `fio_x509_cert_s`, exposing the
subject, issuer, SAN entries, public key, validity, and a SHA-256
fingerprint. `info.verified` reflects OpenSSL's chain verification result
(`SSL_get_verify_result`).
---
## SSL Context Modes
The `SSL_CTX` is configured for non-blocking use:
```
SSL_MODE_ENABLE_PARTIAL_WRITE — SSL_write may return before all data encrypted
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER — buffer pointer may differ between retries
SSL_MODE_RELEASE_BUFFERS — free idle 34 KB per-connection OpenSSL buffers
SSL_MODE_AUTO_RETRY — CLEARED (return immediately on WANT_READ/WRITE)
```
TLS 1.3 session tickets: 2 tickets are configured for session resumption
(saves ~30–50 % of handshake CPU on reconnects).
---
## Internal Design — Custom BIOs
Instead of `BIO_s_mem()` (which bounces data through a private `BUF_MEM`),
the backend uses custom `BIO_METHOD` objects that give OpenSSL direct access to
the per-connection buffers:
- **rbio** — OpenSSL reads raw socket bytes directly from a 64 KB receive
buffer that was filled by a single `fio_sock_read` call.
- **wbio** — OpenSSL appends encrypted output directly into a ~66 KB
encrypted-output buffer (4 × max TLS record).
This eliminates all intermediate copies while retaining full control over
buffering and partial writes.
Handshake responses (ServerHello, etc.) are flushed to the socket from inside
the `read` callback — the IO layer's `on_ready` loop doesn't run until the
handshake completes, so the backend must send them itself.
---
## Lifecycle and Error Behavior
```
fio_io_listen / fio_io_connect
│
└─► build_context (SSL_CTX built once per listener/connector)
│
└─► per-connection start (SSL_new + BIO setup + SSL_accept/SSL_connect)
│
├─ read (advances handshake until SSL_is_init_finished,
│ then SSL_read_ex)
├─ write (SSL_write_ex, encrypt into wbio buffer, flush socket)
├─ flush (send remaining enc_buf bytes)
│
├─ finish (SSL_shutdown + best-effort send of close_notify)
└─ cleanup (SSL_free — also frees both BIOs)
```
**Error codes** surfaced to the IO layer:
- `return 0` — peer closed cleanly (`SSL_ERROR_ZERO_RETURN`) or fatal SSL error.
- `return -1` with `errno = EWOULDBLOCK` — not enough data yet (normal event-loop
signalling; the reactor will retry on next readable event).
All non-fatal internal issues are logged via `FIO_LOG_ERROR` /
`FIO_LOG_WARNING` / `FIO_LOG_SECURITY`. Debug-level detail is at
`FIO_LOG_DDEBUG2`.
---
## Minimal Server Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf));
if (n)
fio_io_write(io, buf, n); /* echo */
}
static fio_io_protocol_s ECHO_PROTO = {
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
/* No certificate configured → self-signed ECDSA P-256 generated automatically */
fio_io_tls_s *tls = fio_io_tls_new();
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &ECHO_PROTO,
.tls = tls);
fio_io_tls_free(tls);
FIO_LOG_INFO("TLS echo server on :8443 (test: openssl s_client -connect localhost:8443)");
fio_io_start(0);
}
```
For a production server, load real certificates:
```c
fio_io_tls_cert_add(tls, "example.com", "cert.pem", "key.pem", NULL);
```
---
## Disambiguation
| Document | Scope |
|----------|-------|
| **This document** | OpenSSL 3.x IO-layer backend — plugs TLS into the reactor |
| [./405 tls13.md](./405 tls13.md) *(planned)* | Native TLS 1.3 IO backend (same `fio_io_functions_s` interface, no OpenSSL dependency) |
| [./190 tls13.md](./190 tls13.md) | Standalone TLS 1.3 crypto library (key schedule, record layer, raw machinery) |
| [./400 io-overview.md](./400 io-overview.md) | IO + TLS stack overview |
# Native TLS 1.3 IO Backend
```c
#define FIO_IO
#include "fio-stl/include.h"
```
IO-layer TLS 1.3 integration — for standalone crypto library see [`190 tls13.md`](./190 tls13.md).
This module wires the native TLS 1.3 state machines into the facil.io IO reactor
as a drop-in transport backend. When OpenSSL is unavailable at build time it
registers itself automatically; otherwise it is available on demand via
`fio_tls13_io_functions()`. No external dependencies required.
For the IO/TLS stack overview see [./400 io-overview.md](./400 io-overview.md).
For the OpenSSL backend (preferred when available) see [./405 openssl.md](./405 openssl.md).
---
## Activation
The module compiles when all of the following are true:
| Condition | Notes |
|-----------|-------|
| `FIO_IO` is defined | pulls in the IO reactor |
| `H___FIO_TLS13___H` guard is satisfied | `190 tls13.h` was included first |
| `FIO_NO_TLS` is **not** defined | opt-out guard checked by `include.h` before this backend is included |
A module-level constructor (`FIO_CONSTRUCTOR`) runs automatically before
`main()` **only when OpenSSL is absent** (`HAVE_OPENSSL` not defined and
`405 openssl.h` not included). It calls:
```c
fio_io_tls_default_functions(&FIO___TLS13_IO_FUNCS);
```
This makes every subsequent `fio_io_listen` / `fio_io_connect` call with a
`tls` argument use the native TLS 1.3 engine without any extra configuration.
When OpenSSL *is* present, the native backend compiles but does **not**
auto-register. Use `fio_tls13_io_functions()` to switch explicitly if needed.
---
## Public API
### `fio_tls13_io_functions`
```c
fio_io_functions_s fio_tls13_io_functions(void);
```
Returns the `fio_io_functions_s` vtable that wires the native TLS 1.3 engine
into the IO reactor:
| Field | Role |
|-------|------|
| `build_context` | Converts `fio_io_tls_s` into a per-listener/connector context |
| `free_context` | Deferred free of the context (via `fio_io_defer`) |
| `start` | Per-connection: allocate state, run ClientHello or await ServerHello |
| `read` | Non-blocking decrypt: advances handshake if needed, then decrypts records |
| `write` | Non-blocking encrypt: batches up to 4 TLS records (64 KB) per syscall |
| `flush` | Drains any pending handshake or encrypted bytes from internal buffers |
| `finish` | Sends a TLS `close_notify` alert before the TCP close |
| `cleanup` | Frees per-connection TLS state |
Normally you never call this directly — the constructor handles registration.
Use it to override the global default or set a per-protocol backend:
```c
/* Override: force native TLS 1.3 even when OpenSSL is present */
fio_io_functions_s tls13_funcs = fio_tls13_io_functions();
fio_io_tls_default_functions(&tls13_funcs);
```
Or set it on a specific protocol without changing the global default:
```c
fio_io_functions_s tls13_funcs = fio_tls13_io_functions();
MY_PROTOCOL.io_functions = tls13_funcs; /* per-protocol override */
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &MY_PROTOCOL,
.tls = tls);
```
---
## Configuring TLS — `fio_io_tls_s`
TLS parameters are carried in a `fio_io_tls_s` object (defined in
`401 io api.h`). Build one before calling `fio_io_listen` or `fio_io_connect`:
```c
/* Allocate (reference counted) */
fio_io_tls_s *tls = fio_io_tls_new();
/* Certificate — PEM files */
fio_io_tls_cert_add(tls,
"www.example.com", /* server_name (SNI) */
"cert.pem", /* public certificate or chain */
"key.pem", /* private key */
NULL); /* PEM password, or NULL */
/* ALPN protocol negotiation */
fio_io_tls_alpn_add(tls, "h2", on_http2_selected);
fio_io_tls_alpn_add(tls, "http/1.1", on_http1_selected);
/* Peer certificate verification */
fio_io_tls_trust_add(tls, NULL); /* use system trust store */
fio_io_tls_trust_add(tls, "ca.pem"); /* or a specific CA bundle */
/* Listen */
fio_io_listen(.url = "0.0.0.0:443",
.protocol = &MY_PROTOCOL,
.tls = tls);
fio_io_tls_free(tls); /* release your reference; the listener holds its own */
fio_io_start(0);
```
`fio_io_tls_s` is reference-counted. The backend duplicates the reference
during `build_context`; freeing yours after `fio_io_listen` is always safe.
---
## Certificates
### Loading from PEM files
When both `public_cert_file` and `private_key_file` are provided to
`fio_io_tls_cert_add`, the backend reads and parses the PEM files, loading
every `CERTIFICATE` block in the file as a DER chain entry. Three private key
types are supported:
| Key type | Constant | Notes |
|----------|----------|-------|
| ECDSA P-256 | `FIO_TLS13_SIGNATURE_ECDSA_SECP256R1_SHA256` | Recommended; small and fast |
| Ed25519 | `FIO_TLS13_SIGNATURE_ED25519` | Fastest signatures |
| RSA (any size) | `FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA256` | Requires `H___FIO_RSA___H` |
If PEM parsing fails, the backend logs `FIO_LOG_WARNING` and falls back to a
self-signed certificate. If the fallback also fails the context build fails
and `build_context` returns `NULL`.
### Self-signed fallback
When a **server** has no certificates configured, the backend generates a
self-signed P-256 ECDSA certificate on the fly using `fio_x509_self_signed_cert`
(requires `H___FIO_X509___H`):
| Property | Value |
|----------|-------|
| Key algorithm | ECDSA P-256 (128-bit security ≈ RSA-3072) |
| Signature | SHA-256 |
| SAN | set to `server_name` (defaults to `"localhost"`) |
Self-signed certificates are fine for development. Browsers will warn. Use a
CA-issued certificate (e.g. Let's Encrypt) in production.
If `H___FIO_X509___H` is not available when no PEM files are configured, the
context build fails with `FIO_LOG_ERROR`.
---
## ALPN
Register protocols with `fio_io_tls_alpn_add` before listen/connect. The
first registered protocol is the preferred default.
The backend collects all registered names into a comma-separated string (up
to 255 bytes per name; 255 characters total plus the NUL terminator). On handshake the server matches the
client's offered list against the registered names in registration order and
calls the corresponding `on_selected` callback on the `fio_io_s *` when
negotiation succeeds.
Protocol names must be 1–255 bytes. The internal list overflows when the
comma-separated list would exceed 255 characters — excess protocols are dropped with `FIO_LOG_ERROR`.
---
## Trust and Peer Verification
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: on **client** connections the backend verifies the server
certificate, on **server** connections it requests, requires, and verifies
the client certificate (mutual TLS). An empty trust list means **no peer
verification** — matching the OpenSSL backend.
| Scenario | Behaviour |
|----------|-----------|
| `fio_io_tls_trust_add(tls, NULL)` | system CA bundle used (loaded once, global) |
| `fio_io_tls_trust_add(tls, "ca.pem")` | user-supplied CA bundle (per-context) |
| No `fio_io_tls_trust_add` call, client mode | no server verification + `FIO_LOG_SECURITY` warning |
| No `fio_io_tls_trust_add` call, server mode | no client certificate requested |
| Trust configured, server mode | client certificate required and verified |
For mTLS, client certificate chains are verified against the trust store
(`fio_x509_verify_chain`) and the client's `CertificateVerify` signature is
validated against the leaf certificate's public key (Ed25519, ECDSA P-256 /
P-384, and RSA-PSS / PKCS#1 SHA-256 / SHA-384 schemes). A client that fails
verification is rejected with a TLS alert during the handshake.
After the handshake, inspect the peer's chain with
`fio_io_peer_info_next(io, &info)` (see the IO API docs) — each call parses
the next certificate in place (zero-copy) into a `fio_x509_cert_s`, exposing
the subject CN/DN, issuer, SAN entries, public key, validity, and a SHA-256
fingerprint for pinning, plus a `verified` flag for authorization decisions.
The system CA bundle is loaded **once** into a process-wide singleton
(`fio___tls13_sys_trust`) and shared read-only across all connections —
loading ~128 certs per outgoing connection is avoided. It is freed at process
exit via a `FIO_CALL_AT_EXIT` callback.
Platform CA bundle lookup order (POSIX):
```
/etc/ssl/cert.pem (macOS, FreeBSD)
/etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu)
/etc/pki/tls/certs/ca-bundle.crt (RHEL/CentOS)
/etc/ssl/ca-bundle.pem (openSUSE)
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (RHEL newer)
/usr/local/etc/ssl/cert.pem (FreeBSD ports)
```
On **Windows** the `ROOT` system certificate store is enumerated via the
CryptoAPI (`CertOpenSystemStoreA` / `CertEnumCertificatesInStore`). On MSVC,
`Crypt32.lib` is linked automatically via `#pragma comment`; other Windows
toolchains may need an explicit `Crypt32` link flag.
---
## Lifecycle and Error Behavior
```
fio_io_listen / fio_io_connect
│
└─► build_context (fio_io_tls_s → fio___tls13_context_s, once per listener)
│
└─► per-connection start
│
├─ client: ClientHello sent immediately
├─ server: awaits ClientHello
│
├─ read (advances handshake records; decrypts app data after)
├─ write (encrypts; up to 4 records batched per syscall)
├─ flush (drains handshake + enc_buf to socket)
│
├─ finish (sends encrypted close_notify alert, best-effort)
└─ cleanup (frees fio___tls13_connection_s)
```
**Internal buffers per connection** (flexible array member, single allocation):
| Region | Purpose | Size |
|--------|---------|------|
| `recv_buf` | Incoming encrypted data | `FIO_IO_BUFFER_PER_WRITE` (~64 KB) |
| `app_buf` | Decrypted plaintext ready to deliver | `FIO_IO_BUFFER_PER_WRITE` |
| `send_buf` | Outgoing handshake bytes | `FIO_IO_BUFFER_PER_WRITE` |
| `enc_buf` | Pre-allocated encryption output (4 max records) | ~66 KB |
**Error codes surfaced to the IO layer:**
- `return 0` from `read` — peer closed cleanly (EOF).
- `return -1` with `errno = EWOULDBLOCK` — no data yet; reactor retries on next readable event.
- `return -1` with `errno = ECONNRESET` — TLS handshake or decryption error; connection will be closed.
All internal errors are logged: `FIO_LOG_ERROR` for hard failures,
`FIO_LOG_WARNING` for soft failures (e.g. PEM fallback), `FIO_LOG_DEBUG2` /
`FIO_LOG_DDEBUG2` for per-connection detail.
**KeyUpdate** (RFC 8446 §4.6.3): when the peer requests a key update, a
`KeyUpdate` response is prepended to the next `write` syscall alongside
application data so they go out in a single call.
---
## Minimal Server Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf));
if (n)
fio_io_write(io, buf, n); /* echo */
}
static fio_io_protocol_s ECHO_PROTO = {
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
/* No certificate → self-signed P-256 generated automatically */
fio_io_tls_s *tls = fio_io_tls_new();
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &ECHO_PROTO,
.tls = tls);
fio_io_tls_free(tls);
FIO_LOG_INFO("TLS echo server on :8443");
fio_io_start(0);
}
```
For a production server, load real certificates:
```c
fio_io_tls_cert_add(tls, "example.com", "cert.pem", "key.pem", NULL);
```
---
## TLS Client Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_attach(fio_io_s *io) {
/* Handshake happens transparently through the TLS transport hooks. */
const char req[] = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
fio_io_write(io, req, sizeof(req) - 1);
}
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf) - 1);
if (n) {
buf[n] = '\0';
FIO_LOG_INFO("response:\n%s", buf);
fio_io_close(io);
}
}
static fio_io_protocol_s CLIENT_PROTO = {
.on_attach = on_attach,
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
fio_io_tls_s *tls = fio_io_tls_new();
/* SNI hostname — system CA store is used automatically for verification */
fio_io_tls_cert_add(tls, "example.com", NULL, NULL, NULL);
fio_io_connect(.url = "example.com:443",
.protocol = &CLIENT_PROTO,
.tls = tls);
fio_io_tls_free(tls);
fio_io_start(0);
}
```
---
## Comparison with OpenSSL Backend
| Feature | Native TLS 1.3 | OpenSSL 3.x |
|---------|---------------|-------------|
| External dependency | None | OpenSSL 3.x |
| TLS versions | 1.3 only | 1.0–1.3 |
| Certificate key types | P-256, Ed25519, RSA | All |
| ALPN | Yes | Yes |
| Self-signed auto-cert | Yes (P-256) | Yes (P-256) |
| System trust store | Yes (multi-platform) | Yes |
| Session resumption (0-RTT) | No | Yes |
| OCSP stapling | No | Yes |
| Binary size impact | Smaller | Larger |
| Auto-registered when | OpenSSL absent | OpenSSL present |
The native backend is suitable for environments where TLS 1.3-only is
acceptable and minimising dependencies matters. If you need legacy TLS
versions, session resumption, OCSP stapling, or full certificate type
coverage, use the OpenSSL backend.
---
## Disambiguation
| Document | Scope |
|----------|-------|
| **This document** | Native TLS 1.3 IO backend — plugs TLS into the reactor via `fio_io_functions_s` |
| [./405 openssl.md](./405 openssl.md) | OpenSSL 3.x IO backend (same interface, preferred when available) |
| [./190 tls13.md](./190 tls13.md) | Standalone TLS 1.3 crypto library — key schedule, record layer, handshake state machines |
| [./400 io-overview.md](./400 io-overview.md) | IO + TLS stack overview |
# Pub/Sub — Publish/Subscribe Messaging (420 pubsub.h)
```c
#define FIO_PUBSUB
#include FIO_INCLUDE_FILE
```
> **Requires:** `FIO_IPC`. When using `include.h` the dependency is resolved automatically. When using the combined `fio-stl.h`, define `FIO_IPC` before `FIO_PUBSUB`.
Pub/Sub delivers messages to named channels across all worker processes and,
through pluggable engines, across machines or external brokers. It is the
broadcast layer that sits directly on top of IPC.
See [./400 io-overview.md](./400 io-overview.md) for where Pub/Sub fits in the
full IO stack, and [./404 ipc.md](./404 ipc.md) for the IPC transport it relies on.
---
## How it fits together
```
publisher (any thread/process)
│
│ fio_pubsub_publish(...)
▼
engine->publish() ← default: fio_pubsub_engine_ipc()
│
│ IPC message (fio_ipc_local / fio_ipc_broadcast)
▼
every process receives it
│
├─ matches channel → subscriber callbacks scheduled on queue
├─ matches patterns → pattern subscriber callbacks scheduled
└─ history manager → message stored for replay
```
Workers do not talk to each other directly. Every publish goes through the
master (via IPC), which fans the message out to all processes. Subscriptions
follow the same path in reverse: when a new channel is first subscribed in a
worker, the worker notifies the master so the master can track routing.
---
## Configuration Macros
Define these **before** including the header to override the defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_PUBSUB_FUTURE_LIMIT_MS` | `60000` | Messages timestamped more than this many ms in the future are not delivered to subscribers immediately; they are handed to history managers instead. |
| `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT` | `1 << 28` (256 MiB) | Default byte ceiling for the built-in in-memory history cache. |
---
## Types
### `fio_pubsub_msg_s` — the message
```c
typedef struct fio_pubsub_msg_s {
fio_io_s *io; /* IO connection owning the subscription (may be NULL) */
void *udata; /* opaque value set on subscribe */
uint64_t timestamp; /* ms since epoch */
uint64_t id; /* unique message ID */
fio_buf_info_s channel; /* channel name — shared, do NOT mutate */
fio_buf_info_s message; /* message payload — shared, do NOT mutate */
int16_t filter; /* numerical namespace; negative values are reserved */
} fio_pubsub_msg_s;
```
Delivered to `on_message` callbacks. The `channel` and `message` buffers are
shared references into the underlying IPC buffer — do not store pointers to
them beyond the callback unless you call `fio_pubsub_msg2ipc` / `fio_ipc_dup`
first.
### `fio_pubsub_subscribe_args_s` — subscribe / unsubscribe arguments
```c
typedef struct {
fio_io_s *io; /* subscription owner (NULL = global) */
fio_buf_info_s channel; /* channel name or pattern */
void (*on_message)(fio_pubsub_msg_s *msg); /* required callback */
void (*on_unsubscribe)(void *udata); /* optional cleanup callback */
void *udata; /* opaque user data for callbacks */
fio_queue_s *queue; /* callback queue; NULL = IO queue */
uintptr_t *subscription_handle_ptr; /* out: handle for manual management */
uint64_t replay_since; /* replay history since this ms timestamp */
int16_t filter; /* numerical namespace (must match publish) */
uint8_t is_pattern; /* non-zero = channel is a glob pattern */
uint8_t master_only; /* non-zero = subscription lives in master */
} fio_pubsub_subscribe_args_s;
```
Both `fio_pubsub_subscribe` and `fio_pubsub_unsubscribe` use this struct. The
macro wrappers let you pass named arguments directly.
**Ownership rules:**
- When `io` is set, the subscription is stored in the IO object's environment
and cancelled automatically when the connection closes. Only one subscription
per `(channel, filter, is_pattern)` triple is allowed per IO.
- When `io` is NULL, the subscription is global — one per
`(channel, filter, is_pattern)` triple for the whole process.
- When `subscription_handle_ptr` is set, the `io` field is ignored and the
handle is written to the pointer. The caller is responsible for calling
`fio_pubsub_unsubscribe(.subscription_handle_ptr = &handle)` explicitly.
Forgetting to do so leaks the subscription.
**If `on_message` is NULL and `io` is set**, the subscription calls
`io->protocol->on_pubsub(msg)` automatically (protocol-level dispatch).
### `fio_pubsub_publish_args_s` — publish arguments
```c
typedef struct {
fio_pubsub_engine_s const *engine; /* NULL = default engine */
fio_io_s *from; /* exclude this IO from receiving the message */
uint64_t id; /* 0 = auto-generate random ID */
uint64_t timestamp; /* 0 = current reactor tick (ms) */
fio_buf_info_s channel; /* target channel name */
fio_buf_info_s message; /* message payload */
int16_t filter; /* numerical namespace */
} fio_pubsub_publish_args_s;
```
---
## Subscribe / Unsubscribe
### `fio_pubsub_subscribe`
```c
void fio_pubsub_subscribe(fio_pubsub_subscribe_args_s args);
/* macro wrapper — enables named arguments: */
#define fio_pubsub_subscribe(...) \
fio_pubsub_subscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
Subscribe to a channel. The actual subscription is queued as a deferred task
on the IO thread, so it is safe to call from any thread.
```c
/* Global subscription */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("events"),
.on_message = handle_event);
/* Per-connection subscription — auto-cancelled on close */
fio_pubsub_subscribe(.io = client_io,
.channel = FIO_BUF_INFO1("user:123"),
.on_message = handle_user_msg,
.udata = user_ctx);
/* Pattern subscription (glob) */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("chat:*"),
.on_message = handle_chat,
.is_pattern = 1);
/* Replay history from the last 5 minutes */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = handle_news,
.replay_since = fio_io_last_tick() - 5*60*1000);
/* Manual handle for explicit control */
uintptr_t sub_handle = 0;
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("ticker"),
.on_message = handle_tick,
.subscription_handle_ptr = &sub_handle);
/* … later … */
fio_pubsub_unsubscribe(.subscription_handle_ptr = &sub_handle);
```
> **Note:** pattern subscriptions do not support history replay (`replay_since`
> is silently ignored for patterns).
> **Note:** `master_only = 1` creates the subscription only in the master
> process and is automatically removed from forked workers after `fork()`.
### `fio_pubsub_unsubscribe`
```c
int fio_pubsub_unsubscribe(fio_pubsub_subscribe_args_s args);
/* macro wrapper: */
#define fio_pubsub_unsubscribe(...) \
fio_pubsub_unsubscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
Remove a subscription. Returns `0` on success, `-1` if not found.
Only `io`, `channel`, `filter`, `is_pattern`, and `subscription_handle_ptr`
are used for matching — `on_message` and `udata` are ignored.
```c
fio_pubsub_unsubscribe(.io = client_io,
.channel = FIO_BUF_INFO1("user:123"));
fio_pubsub_unsubscribe(.channel = FIO_BUF_INFO1("chat:*"),
.is_pattern = 1);
```
---
## Publish
### `fio_pubsub_publish`
```c
void fio_pubsub_publish(fio_pubsub_publish_args_s args);
/* macro wrapper: */
#define fio_pubsub_publish(...) \
fio_pubsub_publish((fio_pubsub_publish_args_s){__VA_ARGS__})
```
Publish a message. Thread-safe, callable from any process or thread. In
workers the default engine routes the message through IPC to the master, which
fans it out to all processes.
```c
/* Simple publish */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("events"),
.message = FIO_BUF_INFO1("something happened"));
/* With namespace filter */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("updates"),
.message = FIO_BUF_INFO2(data, data_len),
.filter = 42);
/* Exclude the sender (echo suppression) */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat:room1"),
.message = FIO_BUF_INFO1("hello"),
.from = sender_io);
/* Cluster-wide (all machines) */
fio_pubsub_publish(.engine = fio_pubsub_engine_cluster(),
.channel = FIO_BUF_INFO1("global"),
.message = FIO_BUF_INFO1("cluster alert"));
```
### `fio_pubsub_defer`
```c
void fio_pubsub_defer(fio_pubsub_msg_s *msg);
```
Call from inside `on_message` to push the *same* callback invocation to the
end of the queue. The message and subscription references are managed
automatically.
```c
void on_message(fio_pubsub_msg_s *msg) {
if (not_ready_yet()) {
fio_pubsub_defer(msg); /* try again later */
return;
}
/* process msg… */
}
```
---
## Namespace Filters
The `filter` field (`int16_t`) is a numerical namespace. Publishers and
subscribers must use the same `filter` value to match. Negative values are
reserved for internal use.
Filters are useful when two unrelated subsystems share channel names. Using
`filter = 1` for one and `filter = 2` for the other keeps messages isolated
without any naming conventions.
```c
/* System 1 uses filter 1 */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("status"), .filter = 1, …);
fio_pubsub_publish (.channel = FIO_BUF_INFO1("status"), .filter = 1, …);
/* System 2 uses filter 2 — completely separate, same channel name */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("status"), .filter = 2, …);
fio_pubsub_publish (.channel = FIO_BUF_INFO1("status"), .filter = 2, …);
```
---
## IO Subscription Helper
### `FIO_ON_MESSAGE_SEND_MESSAGE`
```c
void FIO_ON_MESSAGE_SEND_MESSAGE(fio_pubsub_msg_s *msg);
```
A ready-made `on_message` callback that writes `msg->message` directly to
`msg->io`. Zero-copy: it duplicates the underlying IPC buffer reference and
hands it to the IO write queue.
```c
/* Wire clients to a channel with one line */
fio_pubsub_subscribe(.io = client_io,
.channel = FIO_BUF_INFO1("feed"),
.on_message = FIO_ON_MESSAGE_SEND_MESSAGE);
```
It can also serve as the protocol-level `on_pubsub` callback:
```c
fio_io_protocol_s MY_PROTOCOL = {
.on_attach = my_on_attach,
.on_data = my_on_data,
.on_pubsub = FIO_ON_MESSAGE_SEND_MESSAGE,
};
```
---
## Pattern Matching
### `fio_pubsub_match_fn_set`
```c
void fio_pubsub_match_fn_set(uint8_t (*match_cb)(fio_str_info_s pattern,
fio_str_info_s name));
```
Override the function used to test pattern subscriptions. Pass `NULL` to
restore the default (`fio_glob_match`), which supports `*`, `?`, and `[sets]`.
```c
uint8_t my_regex_match(fio_str_info_s pattern, fio_str_info_s name) {
/* … custom logic … */
return matched ? 1 : 0;
}
fio_pubsub_match_fn_set(my_regex_match);
fio_pubsub_match_fn_set(NULL); /* restore glob */
```
---
## Engines
Engines decide *how* messages are distributed. The default engine sends
messages to the local process group (master + all workers on the current
machine). Swap the engine to reach external brokers or remote machines.
### Built-in engines
| Function | Scope |
|----------|-------|
| `fio_pubsub_engine_ipc()` | master + all workers on **this machine** only |
| `fio_pubsub_engine_cluster()` | master + workers on **all machines** in the cluster |
```c
fio_pubsub_engine_s const *fio_pubsub_engine_ipc(void);
fio_pubsub_engine_s const *fio_pubsub_engine_cluster(void);
```
The default engine starts as `fio_pubsub_engine_ipc()` and is automatically
upgraded to `fio_pubsub_engine_cluster()` at `PRE_START` time when
`fio_ipc_cluster_port()` returns a non-zero value (i.e., when cluster IPC is
configured before `fio_io_start`).
### Default engine accessors
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default(void);
fio_pubsub_engine_s const *fio_pubsub_engine_default_set(
fio_pubsub_engine_s const *engine);
```
`fio_pubsub_engine_default_set(NULL)` restores the auto-selected default
(cluster engine if cluster port is set, IPC engine otherwise).
### Custom engine interface
```c
typedef struct fio_pubsub_engine_s {
void (*detached) (const struct fio_pubsub_engine_s *eng);
void (*subscribe) (const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*psubscribe) (const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*unsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*punsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*publish) (const struct fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg);
} fio_pubsub_engine_s;
```
**Execution context:**
- `publish` — called from any thread or process
- all other callbacks — called from the **master process**, **IO thread** only
Callbacks must not block. Defer slow operations with `fio_io_defer`.
Missing callbacks are filled with safe defaults on `fio_pubsub_engine_attach`: subscription callbacks become no-ops; a NULL `publish` falls back to local IPC delivery.
### `fio_pubsub_engine_attach` / `fio_pubsub_engine_detach`
```c
void fio_pubsub_engine_attach(fio_pubsub_engine_s *engine);
void fio_pubsub_engine_detach(fio_pubsub_engine_s *engine);
```
`attach` registers the engine and immediately notifies it of all existing
channel and pattern subscriptions (via `subscribe` / `psubscribe`).
`detach` removes the engine and calls its `detached` callback. If the detached
engine was the current default, the default is automatically reset.
```c
/* Example: a custom engine that bridges to NATS */
static fio_pubsub_engine_s nats_engine = {
.subscribe = nats_subscribe,
.psubscribe = nats_subscribe, /* reuse for patterns if needed */
.unsubscribe = nats_unsubscribe,
.punsubscribe= nats_unsubscribe,
.publish = nats_publish,
.detached = nats_cleanup,
};
/* attach before or after fio_io_start — both work */
fio_pubsub_engine_attach(&nats_engine);
fio_pubsub_engine_default_set(&nats_engine);
```
See [./422 redis.md](./422 redis.md) *(planned)* for the built-in Redis engine,
which is a production example of this interface.
---
## History / Replay
The history system caches messages for late-joining subscribers. History lives
in the **master process only** and is separate from the engine interface.
When `replay_since` is set on a subscription, the subscriber receives all
cached messages with `timestamp >= replay_since` before live messages begin.
### Built-in in-memory cache
```c
fio_pubsub_history_s const *fio_pubsub_history_cache(size_t size_limit);
```
Returns the built-in cache manager and sets its byte ceiling. The built-in
cache is initialized by default but **not attached automatically**; call
`fio_pubsub_history_attach(fio_pubsub_history_cache(size), priority)` to begin
storing messages for replay.
`size_limit = 0` inherits from environment variables in order:
1. `WEBSITE_MEMORY_LIMIT_MB` (× 1 MiB)
2. `WEBSITE_MEMORY_LIMIT_KB` (× 1 KiB)
3. `WEBSITE_MEMORY_LIMIT` (bytes)
4. `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT` (256 MiB)
When the cache overflows its limit, the oldest messages across all channels
are evicted first.
```c
/* Attach the built-in cache with a 64 MiB ceiling */
fio_pubsub_history_attach(fio_pubsub_history_cache(64 * 1024 * 1024), 100);
```
### Attach / detach history managers
```c
int fio_pubsub_history_attach(const fio_pubsub_history_s *manager,
uint8_t priority);
void fio_pubsub_history_detach(const fio_pubsub_history_s *manager);
```
Multiple managers can be active simultaneously. All receive `push()` on every
published message. For replay, managers are consulted in **descending priority
order** via `oldest()` to find a manager with enough history coverage; `replay()`
is called on the selected manager once.
### Custom history manager interface
```c
typedef struct fio_pubsub_history_s {
void (*detached)(const struct fio_pubsub_history_s *hist);
int (*push) (const struct fio_pubsub_history_s *hist,
fio_pubsub_msg_s *msg);
int (*replay) (const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter,
uint64_t since,
void (*on_message)(fio_pubsub_msg_s *msg, void *udata),
void (*on_done)(void *udata),
void *udata);
uint64_t (*oldest) (const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter);
} fio_pubsub_history_s;
```
**Execution context:** all callbacks — **master process**, **IO thread** only.
Do not block; use `fio_io_defer` for slow operations.
`replay` **must** call `on_done(udata)` when finished (success or failure) —
the IPC reply mechanism depends on it.
`replay` returns `0` if it handled the request. Returning `-1` does **not**
trigger fallback; use `oldest()` to return `UINT64_MAX` when the manager cannot
cover that channel/filter.
`oldest` returns `UINT64_MAX` if no history is available for the channel.
Missing callbacks are replaced with stubs on `fio_pubsub_history_attach`.
### `fio_pubsub_history_push_all`
```c
void fio_pubsub_history_push_all(fio_pubsub_msg_s *msg);
```
Push a message to all attached history managers without delivering it locally.
Use this from a custom engine when the engine receives a message that should be
stored for replay but is not routed through the normal publish path.
---
## Advanced: IPC Buffer Access
### `fio_pubsub_msg2ipc` / `fio_pubsub_ipc2msg`
```c
fio_ipc_s *fio_pubsub_msg2ipc(fio_pubsub_msg_s *msg);
fio_pubsub_msg_s fio_pubsub_ipc2msg(fio_ipc_s *ipc);
```
`fio_pubsub_msg2ipc` returns (and detaches) the underlying `fio_ipc_s` for
the message. Use with `fio_ipc_dup` to keep the buffer alive beyond the
callback, enabling true zero-copy deferral.
`fio_pubsub_ipc2msg` is the inverse: reconstruct a `fio_pubsub_msg_s` from a
raw IPC buffer.
> **Fragile.** These functions bypass the normal lifecycle. Incorrect use will
> cause use-after-free bugs. Prefer `fio_pubsub_defer` for simple deferral.
---
## Execution Context Summary
| Operation | Where it runs |
|-----------|---------------|
| `fio_pubsub_publish` | any thread, any process |
| `fio_pubsub_subscribe` / `fio_pubsub_unsubscribe` | any thread, any process |
| `on_message` callback | IO thread, any process (per `queue`) |
| `on_unsubscribe` callback | IO thread, any process |
| Engine `subscribe` / `unsubscribe` / `psubscribe` / `punsubscribe` | **master**, IO thread |
| Engine `publish` | any thread, any process |
| History `push` / `replay` / `oldest` / `detached` | **master**, IO thread |
Callbacks must **not block** — offload slow work with `fio_io_defer` or
route to a background `fio_io_async_s` pool.
---
## Examples
### Chat server — per-connection subscription
```c
#define FIO_PUBSUB
#include FIO_INCLUDE_FILE
static void on_data(fio_io_s *io) {
char buf[256];
size_t len = fio_io_read(io, buf, sizeof(buf) - 1);
if (!len) return;
/* Publish whatever the client sends to the "chat" channel */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat"),
.message = FIO_BUF_INFO2(buf, len),
.from = io); /* suppress echo */
}
static void on_attach(fio_io_s *io) {
/* Every new client gets live chat messages forwarded to it */
fio_pubsub_subscribe(.io = io,
.channel = FIO_BUF_INFO1("chat"),
.on_message = FIO_ON_MESSAGE_SEND_MESSAGE);
}
static fio_io_protocol_s CHAT_PROTOCOL = {
.on_attach = on_attach,
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
fio_io_listen(.url = "0.0.0.0:3000", .protocol = &CHAT_PROTOCOL);
fio_io_start(4); /* 4 worker processes */
return 0;
}
```
### Pattern subscription
```c
static void on_any_chat(fio_pubsub_msg_s *msg) {
printf("[%.*s]: %.*s\n",
(int)msg->channel.len, msg->channel.buf,
(int)msg->message.len, msg->message.buf);
}
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("chat:*"),
.on_message = on_any_chat,
.is_pattern = 1);
/* Matches "chat:room1", "chat:lobby", "chat:anything" */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat:room1"),
.message = FIO_BUF_INFO1("hello room"));
```
### History replay (late subscriber)
```c
static void on_news(fio_pubsub_msg_s *msg) {
printf("[%llu] %.*s\n",
(unsigned long long)msg->timestamp,
(int)msg->message.len, msg->message.buf);
}
/* Subscribe and receive all messages from the last 10 minutes */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = on_news,
.replay_since = fio_io_last_tick() - 10*60*1000);
```
Attach the built-in cache, or add a custom persistence layer:
```c
/* Attach built-in cache with a 32 MiB ceiling */
fio_pubsub_history_attach(fio_pubsub_history_cache(32 * 1024 * 1024), 50);
/* Attach a custom database-backed manager at higher priority */
fio_pubsub_history_attach(&my_db_history_manager, 100);
```
### Cluster-wide publish
```c
int main(void) {
/* Enable cluster RPC on port 9999 — must be set before fio_io_start */
fio_ipc_cluster_listen(9999);
/* Default engine auto-upgrades to cluster engine at PRE_START */
fio_io_start(4);
return 0;
}
/* Anywhere, after startup — goes to every machine in the cluster */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("invalidate"),
.message = FIO_BUF_INFO1("cache_key_42"));
/* Or explicitly select the engine */
fio_pubsub_publish(.engine = fio_pubsub_engine_cluster(),
.channel = FIO_BUF_INFO1("invalidate"),
.message = FIO_BUF_INFO1("cache_key_42"));
```
### Custom engine (skeleton)
```c
static void my_engine_subscribe(const fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter) {
/* tell external broker to subscribe */
(void)eng; (void)filter;
external_broker_subscribe(channel.buf, channel.len);
}
static void my_engine_publish(const fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg) {
/* forward to external broker */
(void)eng;
external_broker_publish(msg->channel.buf, msg->channel.len,
msg->message.buf, msg->message.len);
}
static void my_engine_cleanup(const fio_pubsub_engine_s *eng) {
(void)eng;
external_broker_disconnect();
}
static fio_pubsub_engine_s my_engine = {
.subscribe = my_engine_subscribe,
.psubscribe = my_engine_subscribe, /* same logic for patterns, or differentiate */
.unsubscribe = NULL, /* NULL → filled with no-op on attach */
.punsubscribe = NULL,
.publish = my_engine_publish,
.detached = my_engine_cleanup,
};
/* Register before or after fio_io_start */
fio_pubsub_engine_attach(&my_engine);
fio_pubsub_engine_default_set(&my_engine);
```
---
## Related
- [./400 io-overview.md](./400 io-overview.md) — full IO/IPC/PubSub stack diagram
- [./404 ipc.md](./404 ipc.md) — IPC transport, `fio_ipc_cluster_listen`, `fio_ipc_s` lifetime
- [./422 redis.md](./422 redis.md) *(planned)* — Redis as a pub/sub engine and command client
# Redis — Pub/Sub Engine and Command Client (422 redis.h)
```c
#define FIO_REDIS
#include FIO_INCLUDE_FILE
```
> **Requires:** `FIO_IO`, `FIO_PUBSUB`, `FIO_FIOBJ`, `FIO_RESP3`. When using `include.h` all dependencies are resolved automatically.
The Redis module does two things: it acts as a **Pub/Sub engine** that connects facil.io's Pub/Sub system to a Redis server, enabling cross-machine message distribution; and it acts as a **Redis command client** for arbitrary commands (`GET`, `SET`, `INCR`, etc.).
See [./400 io-overview.md](./400 io-overview.md) for where Redis fits in the full IO stack, [./420 pubsub.md](./420 pubsub.md) for the Pub/Sub engine interface, [./404 ipc.md](./404 ipc.md) for the IPC transport workers use to reach the master, and [./250 fiobj.md](./250 fiobj.md) for how FIOBJ types map to RESP replies.
---
## Architecture
Only the **master** process opens a TCP connection to Redis. Workers never connect directly. Instead:
- Workers forward `fio_redis_send()` calls to the master via IPC; the master executes the command and replies back.
- Workers forward `fio_pubsub_publish()` calls to the master via IPC; the master sends `PUBLISH` to Redis.
- Incoming subscription messages arrive as RESP3 push frames on the master's connection and are fanned out to all workers via the normal Pub/Sub IPC infrastructure.
**RESP3 is required (Redis ≥ 6.0).** The engine negotiates the protocol with a `HELLO 3` handshake on every (re)connection — authentication folds into `HELLO`. There is **no RESP2 fallback**: if the handshake fails (old server, bad credentials), the engine logs a hard error and stops.
After `HELLO 3`, `SUBSCRIBE` no longer commandeers the connection, so the master maintains a **single TCP connection** per engine:
| Traffic | Handling |
|---|---|
| Commands (`fio_redis_send`), `PUBLISH`, `PING`, `HELLO` | Lock-step queue (`should_wait`) — the next command is sent only after the reply arrives |
| `SUBSCRIBE` / `PSUBSCRIBE` / `UNSUBSCRIBE` / `PUNSUBSCRIBE` | Same queue, no-wait — classified by verb at accept time, popped as soon as they are sent; confirmations arrive as RESP3 **push frames**, never command replies, so they cannot desynchronize the queue |
| Incoming pub/sub messages | RESP3 **push frames**, routed to the push handler and re-published locally via `fio_pubsub_engine_ipc()` (zero-copy from the read buffer) |
All outgoing bytes flow through the single command queue. `HELLO` is always the first command on the wire of every (re)connection (queued at the head in `on_attach`, before the resubscribe batches and any leftover commands), and nothing is written while the TCP connection is still in progress (`conn.ready` gate).
```
Master Process
┌──────────────────────────────────────────┐
│ connection ─────── Redis Server │
│ (commands / PUBLISH / PING / HELLO │
│ + SUBSCRIBE family / push frames) │
└──────────────────────────────────────────┘
▲ │
│ IPC │ pub/sub IPC fan-out
│ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Worker 1 │ │ Worker 2 │ │ Worker N │
└────────────┘ └────────────┘ └────────────┘
```
In single-process mode (`fio_io_start(0)`) the process is both master and worker; all operations go directly to Redis with no IPC overhead.
---
## Setup
```c
#define FIO_LOG
#define FIO_REDIS
#include FIO_INCLUDE_FILE
int main(void) {
/* Create the engine BEFORE fio_io_start() (before fork). */
fio_pubsub_engine_s *redis = fio_redis_new(
.url = "redis://localhost:6379",
.auth = "my_password" /* optional */
);
/* Dup before attach — attach transfers your reference to the system. */
fio_redis_dup(redis); /* ref: 1 → 2 */
fio_pubsub_engine_attach(redis); /* system takes the original ref */
fio_io_start(4); /* master connects; workers use IPC */
fio_pubsub_engine_detach(redis); /* system releases its ref: 2 → 1 */
fio_redis_free(redis); /* caller releases dup: 1 → 0 → destroy */
}
```
`fio_redis_new()` **must** be called before `fio_io_start()`. Creating an engine from a worker process is not supported.
---
## Configuration
### `FIO_REDIS_READ_BUFFER`
```c
#define FIO_REDIS_READ_BUFFER 65536 /* default: 64 KiB */
```
Read buffer size for the connection. The master allocates `FIO_REDIS_READ_BUFFER` bytes of buffer space inside each engine. Note that large replies do **not** need to fit the buffer: blob strings larger than `FIO_RESP3_STREAM_THRESHOLD` (see [./004 resp3.md](./004 resp3.md)) are streamed incrementally. The effective size cap is `payload_limit` (see below).
### `FIO_REDIS_MAX_BATCH`
```c
#define FIO_REDIS_MAX_BATCH 128 /* default: 128 messages */
```
Maximum number of complete messages processed per `on_data` event. When the cap is reached with more data buffered, processing continues in a deferred task, keeping any single event-loop callback small.
---
## Types
### `fio_redis_args_s`
```c
typedef struct {
const char *url; /* Redis server URL; NULL → "localhost:6379" */
const char *auth; /* Password for HELLO ... AUTH default ; NULL = no auth */
size_t auth_len; /* Length of auth; 0 = strlen(auth) */
uint8_t ping_interval;/* Keepalive interval in seconds; 0 → 30 s */
size_t payload_limit;/* Per-message budget in bytes; 0 → 16 MiB */
} fio_redis_args_s;
```
**`url`** accepted formats:
| Format | Example |
|---|---|
| Scheme with port | `"redis://host:6380"` |
| Scheme, default port | `"redis://host"` |
| Host and port | `"host:6380"` |
| Host only | `"myredis"` |
| NULL or `""` | → `localhost:6379` |
**`ping_interval`** — the IO reactor sends a `PING` on the connection if it has been idle this many seconds. Default: **30 seconds**. The protocol error timeout (detecting a hung connection) is also governed by this value.
**`payload_limit`** — cumulative payload budget **per top-level Redis message**, in bytes. Default: **16 MiB** (`16 << 20`). The budget is charged as:
```
total = Σ(all string payload bytes) + 32 × (count of ALL objects)
```
Every object in the reply (String, Array, Map, Number, Bool, etc.) costs 32 bytes of budget; string payload bytes are charged in full — for fixed-length blob strings, **before** the buffer is allocated. A breach logs an error and disconnects the engine. This protects against hostile or corrupt servers declaring huge `$` allocations or sending oversized replies. Pub/sub push frames share the same per-message budget.
---
## Reference Counting and Ownership
The engine uses reference counting (`FIO_REF`). Three things independently hold or transfer refs:
1. **Caller**: `fio_redis_new()` returns `ref = 1`. The caller owns this ref and must release it with `fio_redis_free()`.
2. **Pub/Sub system**: `fio_pubsub_engine_attach()` prepares the engine for Pub/Sub use. The Redis engine keeps its Pub/Sub reference on first `SUBSCRIBE` / `PSUBSCRIBE` and releases it from `on_detached`; call `fio_redis_dup()` before attach if you need to keep using the pointer after attaching.
3. **Internal deferred tasks**: The engine dups before scheduling an IO-deferred task and frees at the end. These are invisible to the caller.
**Usage patterns:**
```c
/* Pattern A — with Pub/Sub */
fio_pubsub_engine_s *redis = fio_redis_new(...); /* ref = 1 */
fio_redis_dup(redis); /* ref = 2 (keep a copy) */
fio_pubsub_engine_attach(redis); /* system takes original ref */
/* ...use pub/sub... */
fio_pubsub_engine_detach(redis); /* system releases → ref = 1 */
fio_redis_free(redis); /* caller releases → ref = 0 → destroy */
/* Pattern B — command client only, no pub/sub */
fio_pubsub_engine_s *redis = fio_redis_new(...); /* ref = 1 */
/* ...send commands... */
fio_redis_free(redis); /* ref = 0 → destroy */
```
You do **not** have to detach before freeing. If you free while the pub/sub system still holds its ref, the engine remains alive until detach (or shutdown) fires the `on_detached` callback.
---
## API
### `fio_redis_new`
```c
fio_pubsub_engine_s *fio_redis_new(fio_redis_args_s args);
#define fio_redis_new(...) fio_redis_new((fio_redis_args_s){__VA_ARGS__})
```
Creates a Redis engine with `ref = 1`. The macro overload enables named arguments.
Returns a pointer to `fio_pubsub_engine_s` on success, `NULL` on allocation failure.
The engine is typed as `fio_pubsub_engine_s *` so it can be passed directly to `fio_pubsub_engine_attach()`. Pass the same pointer to `fio_redis_dup()`, `fio_redis_free()`, and `fio_redis_send()`.
The connection is deferred to the IO reactor; the engine does nothing until `fio_io_start()` is called.
```c
fio_pubsub_engine_s *redis = fio_redis_new(
.url = "redis://10.0.0.5:6379",
.auth = "s3cr3t",
.ping_interval = 60
);
```
### `fio_redis_dup`
```c
fio_pubsub_engine_s *fio_redis_dup(fio_pubsub_engine_s *engine);
```
Atomically increments the reference count and returns the engine. Returns `NULL` if `engine` is `NULL`.
Each `fio_redis_dup()` must be balanced with a `fio_redis_free()`.
### `fio_redis_free`
```c
void fio_redis_free(fio_pubsub_engine_s *engine);
```
Releases the caller's reference. When the count reaches zero, destroys the engine immediately: sets `running = 0`, closes the connection, drains the command queue (invoking any pending callbacks with `FIOBJ_INVALID`), and frees memory.
Safe to call with `NULL` (no-op).
### `fio_redis_state`
```c
typedef enum {
FIO_REDIS_STATE_ERROR,
FIO_REDIS_STATE_CONNECTING,
FIO_REDIS_STATE_CONNECTED,
} fio_redis_state_e;
fio_redis_state_e fio_redis_state(const fio_pubsub_engine_s *engine);
```
Returns a Redis connection-state snapshot when called from the IO thread:
| State | Meaning |
|---|---|
| `FIO_REDIS_STATE_ERROR` | `engine` is `NULL`, or `HELLO 3` failed and disabled the engine. |
| `FIO_REDIS_STATE_CONNECTING` | No TCP socket is attached, or the socket is awaiting its `HELLO 3` reply. |
| `FIO_REDIS_STATE_CONNECTED` | The TCP socket is attached and its RESP3 `HELLO 3` handshake completed. |
This is an observability API; callers may send commands in every state. Commands queue until the handshake completes.
### `fio_redis_send`
```c
int fio_redis_send(fio_pubsub_engine_s *engine,
FIOBJ command,
void (*callback)(fio_pubsub_engine_s *e,
FIOBJ reply,
void *udata),
void *udata);
```
Sends a Redis command. `command` must be a `FIOBJ_T_ARRAY` whose elements are the command verb and arguments as `FIOBJ_T_STRING` (or numbers). `callback` is optional (pass `NULL` to fire-and-forget).
Returns `0` on success, `-1` if `engine` is `NULL` or `command` is not a FIOBJ array.
**On the master**: the command is serialized to RESP and queued on the connection. Commands are sent one at a time (lock-step); the next command is sent after the reply for the previous one arrives.
**On a worker**: the RESP bytes are forwarded to the master via IPC. The master executes the command, serializes the reply to RESP, and sends it back. The worker deserializes and calls the callback on its IO thread. This is transparent to the caller.
**Never** pass `SUBSCRIBE`, `PSUBSCRIBE`, `UNSUBSCRIBE`, or `PUNSUBSCRIBE` to `fio_redis_send()`. Subscriptions must go through `fio_pubsub_subscribe()` so the Pub/Sub system tracks them (local delivery, resubscription on reconnect). Direct sends bypass that bookkeeping and their confirmations (RESP3 push frames) are ignored.
**Callback signature:**
```c
void my_callback(fio_pubsub_engine_s *e, FIOBJ reply, void *udata);
```
`reply` is `FIOBJ_INVALID` if the engine was destroyed or the connection was lost before the reply arrived. Otherwise it is a FIOBJ object:
| Redis response | FIOBJ type |
|---|---|
| Bulk string / simple string | `FIOBJ_T_STRING` |
| Integer | `FIOBJ_T_NUMBER` |
| Array | `FIOBJ_T_ARRAY` |
| Map (RESP3) | `FIOBJ_T_HASH` |
| Null | `fiobj_null()` |
| Boolean (RESP3) | `fiobj_true()` / `fiobj_false()` |
| Double (RESP3) | `FIOBJ_T_FLOAT` |
| Bignum (RESP3) | `FIOBJ_T_STRING` |
| Error | `FIOBJ_T_STRING` (warning logged) |
The callback owns nothing — `reply` is freed by the engine after the callback returns. Copy any data you need to keep.
---
## Building Commands
Commands are FIOBJ arrays of strings (and optionally numbers). Build them, pass to `fio_redis_send()`, then free them — the engine serializes to RESP before returning.
```c
/* SET mykey "hello" */
FIOBJ cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("SET", 3));
fiobj_array_push(cmd, fiobj_str_new_cstr("mykey", 5));
fiobj_array_push(cmd, fiobj_str_new_cstr("hello", 5));
fio_redis_send(redis, cmd, NULL, NULL); /* fire-and-forget */
fiobj_free(cmd);
/* GET mykey */
static void on_get(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
fio_str_info_s s = fiobj2cstr(reply);
printf("value: %.*s\n", (int)s.len, s.buf);
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("GET", 3));
fiobj_array_push(cmd, fiobj_str_new_cstr("mykey", 5));
fio_redis_send(redis, cmd, on_get, NULL);
fiobj_free(cmd);
/* INCR counter */
static void on_incr(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
printf("counter now: %ld\n", (long)fiobj2i(reply));
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("INCR", 4));
fiobj_array_push(cmd, fiobj_str_new_cstr("counter", 7));
fio_redis_send(redis, cmd, on_incr, NULL);
fiobj_free(cmd);
```
Array replies (e.g., `LRANGE`, `HGETALL`):
```c
static void on_hgetall(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
size_t n = fiobj_array_count(reply);
for (size_t i = 0; i + 1 < n; i += 2) {
fio_str_info_s k = fiobj2cstr(fiobj_array_get(reply, (int32_t)i));
fio_str_info_s v = fiobj2cstr(fiobj_array_get(reply, (int32_t)(i + 1)));
printf(" %.*s = %.*s\n", (int)k.len, k.buf, (int)v.len, v.buf);
}
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("HGETALL", 7));
fiobj_array_push(cmd, fiobj_str_new_cstr("myhash", 6));
fio_redis_send(redis, cmd, on_hgetall, NULL);
fiobj_free(cmd);
```
See [./250 fiobj.md](./250 fiobj.md) for FIOBJ construction and inspection helpers.
---
## Pub/Sub Integration
When attached via `fio_pubsub_engine_attach()`, the Redis engine implements the `fio_pubsub_engine_s` interface:
| Pub/Sub event | Redis action |
|---|---|
| New channel subscription | `SUBSCRIBE channel` (fire-and-forget) |
| New pattern subscription | `PSUBSCRIBE pattern` (fire-and-forget) |
| Channel unsubscribe | `UNSUBSCRIBE channel` (fire-and-forget) |
| Pattern unsubscribe | `PUNSUBSCRIBE pattern` (fire-and-forget) |
| `fio_pubsub_publish(...)` | `PUBLISH channel message` through the command queue (workers route via IPC) |
| Incoming Redis push message | Re-published locally with `fio_pubsub_engine_ipc()` to fan out to all processes (zero-copy from the read buffer) |
The `filter` parameter from facil.io's Pub/Sub system is ignored by the Redis engine — Redis does not support numeric filter namespaces.
Example: subscribing to Redis channels through facil.io's Pub/Sub API:
```c
static fio_pubsub_engine_s *redis_engine; /* global, set before fork */
static void on_message(fio_pubsub_msg_s *msg) {
printf("channel: %.*s message: %.*s\n",
(int)msg->channel.len, msg->channel.buf,
(int)msg->message.len, msg->message.buf);
}
/* Called after IO reactor starts (FIO_CALL_ON_START) */
static void on_start(void *udata) {
/* Regular subscription — Redis engine sends SUBSCRIBE */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("alerts"),
.on_message = on_message);
/* Pattern subscription — Redis engine sends PSUBSCRIBE */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("events:*"),
.on_message = on_message,
.is_pattern = 1);
/* Publish via Redis — specify the engine to route through Redis PUBLISH.
* Without .engine the default IPC engine is used (local cluster only). */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("alerts"),
.message = FIO_BUF_INFO1("server started"),
.engine = redis_engine);
(void)udata;
}
int main(void) {
redis_engine = fio_redis_new(.url = "localhost:6379");
fio_redis_dup(redis_engine); /* keep a copy before attaching */
fio_pubsub_engine_attach(redis_engine); /* system takes the original ref */
fio_state_callback_add(FIO_CALL_ON_START, on_start, NULL);
fio_io_start(4);
fio_pubsub_engine_detach(redis_engine);
fio_redis_free(redis_engine);
}
```
See [./420 pubsub.md](./420 pubsub.md) for the full Pub/Sub API and engine interface.
---
## Multi-Process Behavior
The IPC routing is automatic and transparent:
| Call | On master | On worker |
|---|---|---|
| `fio_redis_send()` | Queued on the connection | Serialized → IPC → master → Redis → IPC reply → worker callback |
| `fio_pubsub_publish()` | `PUBLISH` through the command queue | Forwarded via IPC; master sends `PUBLISH` |
| Subscription messages | Received as RESP3 push frames, fanned out via Pub/Sub IPC | Delivered by Pub/Sub IPC from master |
Detection uses `fio_io_is_master()`. In single-process mode `fio_io_start(0)` everything uses the master path.
Worker processes inherit the engine pointer after fork. A `FIO_CALL_IN_CHILD` hook swaps the engine's `publish` vtable pointer to the IPC-forwarding implementation, so workers never accidentally write directly to a Redis socket.
See [./404 ipc.md](./404 ipc.md) for IPC internals.
---
## Reconnect and Failure Behavior
- **Connection loss**: The `on_close` callback logs a warning, silently purges any stale queued `HELLO` commands (a fresh one is queued on reconnect), and schedules a reconnect via a 1s single-shot timer. The delay prevents hot reconnect loops against peers that accept and instantly drop.
- **Connection attempt failure**: A connect that fails before attach (e.g., connection refused) is reported through the IO layer's `on_failed` callback (the redis protocol's `on_close` only fires for established connections). The dangling `conn.io` handle is cleared and the same 1s retry timer is scheduled. A synchronous `fio_io_connect` failure takes the same path. The failed io is reactor-owned and freed by the IO layer's connecting-protocol cleanup - the engine never frees it.
- **Handshake**: Every (re)connection starts with `HELLO 3` (auth folded in) as the first queued command; its map reply is consumed by the normal reply FIFO. Handshake failure is a hard engine error (the engine stops; there is no RESP2 fallback).
- **Queued commands**: Commands in the queue on the master stay queued and are flushed once the connection is re-established (after `HELLO`).
- **Resubscription**: On every (re)connection, the engine iterates the current Pub/Sub channel maps and queues all active `SUBSCRIBE` / `PSUBSCRIBE` commands at the queue head (batched into a static buffer, one no-wait queue node per flushed batch), right after `HELLO`. This restores subscription state without touching ref counts or re-attaching the engine.
- **Keepalive**: A `PING` is queued on the connection after `ping_interval` seconds idle. If the connection has unacknowledged commands when the timeout fires, the connection is forcibly closed and reconnected.
- **Engine destroyed while commands are pending**: All queued commands have their callbacks invoked immediately with `reply = FIOBJ_INVALID`.
- **Retry suppression**: Reconnect timers are never scheduled after engine destroy (`running == 0`) or during reactor shutdown, so a close caused by destroy or shutdown does not resurrect the connection.
---
## Thread Safety
All public API functions are thread-safe:
| Function | Mechanism |
|---|---|
| `fio_redis_new()` | Defers connection to IO thread |
| `fio_redis_dup()` | Atomic reference count increment |
| `fio_redis_free()` | Atomic reference count decrement; destroy runs on caller thread |
| `fio_redis_send()` | Defers queue insertion to IO thread (master); IPC on workers |
Internal state (command queue, connection pointers) is only accessed from the IO thread. No locks are needed internally.
FIOBJ objects passed to callbacks are **not** thread-safe. Copy any data you need to keep or use outside the callback.
---
## Limitations
- `fio_redis_new()` must be called before `fio_io_start()` (i.e., before fork). Creating engines from worker processes is not supported.
- **RESP3 required**: Redis >= 6.0 (or a RESP3-capable server such as Valkey). RESP2-only servers and RESP2-only proxies are not supported (the `HELLO 3` handshake fails hard).
- Redis's numeric filter namespaces (`filter` field in Pub/Sub) are not supported. All Redis pub/sub operates with `filter = 0`.
- Single-node Redis only. Redis Cluster requires connecting to the correct shard or using a proxy.
- Replies and push messages are bounded by `payload_limit` (default 16 MiB cumulative per message), not by `FIO_REDIS_READ_BUFFER` — blob strings larger than the read buffer are streamed incrementally.
- Chunked (`$?`) strings are rejected inside push frames (Redis never emits them; the command-reply path supports them).
# HTTP Module
```c
#define FIO_HTTP
#include FIO_INCLUDE_FILE
```
`FIO_HTTP` adds the higher-level HTTP service built on the facil.io IO layer,
the HTTP handle, the HTTP/1.x parser, the WebSocket parser, and the SSE /
WebSocket glue code.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the
[HTTP/1.x parser](./004 http1 parser.md), the
[WebSocket parser](./004 websocket parser.md), and optional compression
support in [DEFLATE / Gzip](./162 deflate.md).
---
## What Gets Added
`FIO_HTTP` exposes:
- `fio_http_settings_s` and `fio_http_listener_s`.
- server APIs: `fio_http_listen`, `fio_http_listener_settings`,
`fio_http_route`, and `fio_http_route_settings`.
- client APIs: `fio_http_connect` and the `fio_http_websocket_connect`
convenience wrapper.
- request routing helper: `fio_http_resource_action` and
`fio_http_resource_action_e`.
- upgrade helpers for WebSocket and SSE connections.
- pub/sub helpers for upgraded connections.
- handle access helpers: `fio_http_io` and `fio_http_settings`.
- the HTTP handle API (handle and core implementation in
`432 http types.h`), including request / response fields, headers,
cookies, body storage, writing, static-file responses, logging, MIME
lookup, and controller hooks.
Implementation helpers named `fio___...` are private implementation details.
---
## Module Files
All HTTP module files share the `FIO_HTTP` flag.
- `430 http api.h` — public declarations: settings, listener / route /
client APIs, and the full handle API.
- `432 http types.h` — internal types plus the handle and core
implementation (header cache, body storage, static-file responses).
- `434 http accept.h` — accept path, request dispatchers, upgrade
authorization.
- `434 http1.h` — HTTP/1.1 request / response glue, protocol and
controller.
- `434 sse.h` — EventSource (SSE) upgrade, helpers, protocol, controller.
- `434 websocket.h` — WebSocket upgrade, events, protocol, write,
controller.
- `438 http.h` — listen / connect glue, protocol wiring, shared helpers.
- `439 http.h` — cleanup tail (the module's only `#undef FIO_HTTP` site).
The parsers are separate modules: [`004 http1 parser.h`](./004 http1 parser.md)
and [`004 websocket parser.h`](./004 websocket parser.md).
---
## Connection Model
HTTP/1.x requests and client responses are parsed into `fio_http_s` handles.
A response line received by a server connection (or an unsolicited response
received by a client) is logged as a `SECURITY` event and the connection is
closed.
Malformed requests receive a `400 Bad Request` response and the connection is
closed. This includes request framing that could be misread as a pipelined
(smuggled) request: a `Transfer-Encoding` without a single, final `chunked`
coding (RFC 9112 §6.3, §7), and an HTTP/1.1 request without exactly one `Host`
header (RFC 9112 §3.2). Other transfer codings before the final `chunked`
(i.e., `gzip, chunked`) stay visible to the application as a request
`transfer-encoding` header (i.e., `gzip`); the body is passed through as
received, still encoded.
The HTTP layer suspends the IO object while the user callback is running and
resumes it after the response is finished or the upgrade is installed.
Plain HTTP cycles use:
1. request / response parsing;
2. optional `pre_http_body` before an `Expect: 100-continue` body is read;
3. `on_http` for application logic;
4. `fio_http_write` / `fio_http_finish` to send the response;
5. `on_finish` when the non-upgraded cycle is complete.
For non-upgraded server HTTP/1 cycles, a request `Connection` field containing
the case-insensitive `close` token closes after the complete response drains;
subsequent pipelined requests are not dispatched. HTTP/1.0 requests are not
persistent unless a request `Connection` field contains `keep-alive` (RFC 9112
§9.3); the server confirms persistence with a `connection: keep-alive`
response header (unless the application set a `connection` header). Application-initiated closure
remains explicit: schedule the complete response, then call `fio_http_close`
while the handle is valid (i.e., from `on_finish`). Setting only a response
`Connection: close` header does not itself schedule closure.
WebSocket and SSE cycles use the authentication callbacks before upgrade, then
`on_open`, message / event callbacks, `on_ready`, `on_shutdown`, `on_close`, and
finally `on_finish` when the upgraded connection closes.
User callbacks are scheduled through the selected HTTP task queue. If
`fio_http_settings_s.queue` is not supplied, the current IO queue is used.
---
## Settings and Listener API
### `fio_http_settings_s`
```c
typedef struct fio_http_settings_s fio_http_settings_s;
```
`fio_http_settings_s` is used by server listeners, routes, and client
connections. The function-style APIs are shadowed by named-argument macros, so
settings are usually passed as designated initializers.
Callback fields:
| Field | Used for |
| --- | --- |
| `pre_http_body` | Called before body upload when the request used `Expect`. |
| `on_http` | Server request callback, or client response callback. |
| `on_finish` | Called when a plain request / response cycle completes, and after upgraded close cleanup. |
| `on_authenticate_sse` | Return non-zero to reject an SSE upgrade. |
| `on_authenticate_websocket` | Return non-zero to reject a WebSocket upgrade. |
| `on_open` | Called once a WebSocket or SSE upgrade is complete. |
| `on_message` | Called with complete WebSocket messages. |
| `on_eventsource` | Called with complete SSE events. |
| `on_eventsource_reconnect` | Called for SSE requests with `Last-Event-ID`. |
| `on_ready` | Called when an upgraded connection's outgoing buffer is empty. |
| `on_shutdown` | Called for open upgraded connections during IO shutdown. |
| `on_close` | Called after an upgraded connection is closed. |
| `on_stop` | Called when HTTP service settings are being closed. |
Other settings:
| Field | Used for |
| --- | --- |
| `udata` | Default `fio_http_udata(h)` value. |
| `tls_io_func`, `tls` | Optional TLS support. |
| `queue` | Optional HTTP task queue. |
| `public_folder` | Static-file root. Serves pre-compressed `.br`, `.zstd`, `.gz`, `.zip` variants when the client accepts them and the variant is fresh. With `compress_static`, missing `.br`/`.gz` variants are also **created on demand and written into this folder** — the folder must be writable and quota'd (attacker-triggerable disk writes). Prefer pre-generating variants at deploy time. The folder must exist when the listener starts, otherwise the setting is ignored. See *Static File Service* below. |
| `max_age` | Static-file `Cache-Control` max-age value, in seconds. `0` (default) omits the header. Not inherited by routes. |
| `max_header_size` | Maximum combined request line and header bytes. |
| `max_line_len` | Maximum bytes per request / header line. |
| `max_body_size` | Maximum request body size. |
| `ws_max_msg_size` | Maximum buffered WebSocket message size. |
| `reserved1`, `reserved2` | Reserved for future use. |
| `timeout` | HTTP/1.x connection timeout, in seconds. |
| `ws_timeout` | WebSocket timeout; timeout pings are sent. |
| `sse_timeout` | SSE timeout; timeout pings are sent. |
| `connect_timeout` | Client connection timeout. |
| `log` | Enables HTTP request logging. |
| `compress_static` | Opt-in static-file compression (on-demand `.br`/`.gz` creation). Per-route; routes inherit the listener's root value at route-creation. The value is a failure-memoization shift register (see *Static File Service*). Detached handles gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` cflag instead. See the compression security note below. |
| `compress_dynamic` | Opt-in dynamic response compression. Connection-global; per-route values are ignored. See the compression security note below. |
| `compress_ws` | Opt-in WebSocket `permessage-deflate`. Connection-global; per-route values are ignored. See the compression security note below. |
**Compression security note (BREACH/CRIME oracle risk):** compressing
secrets together with attacker-reflected data enables length-oracle attacks
(BREACH/CRIME). Do not enable compression for responses that mix secrets
(CSRF tokens, session data) with attacker-controlled reflections, or emit
such messages uncompressed per call (per-message opt-out: keep the cflag off
at the route level for sensitive routes, or pre-set `content-encoding`
— which the dynamic path always respects — to pass a body through
uncompressed).
**WebSocket `permessage-deflate` negotiation:** the server ALWAYS responds
with `permessage-deflate; server_no_context_takeover;
client_no_context_takeover` (RFC 7692 allows either endpoint to request the
flags unilaterally), honoring `server_max_window_bits` when offered (the
compressor clamps its distances accordingly) and never emitting window-bits
parameters. Cross-message history is never used, so persistent compression
state per connection is ~0 (two ~32-byte contexts plus a bounded input
buffer, shrunk on every message reset); compressor scratch comes from a
static process-wide slot pool checked out per call — under extreme
contention a message simply goes out uncompressed.
**Accept-Encoding handling:** `q=0` (in any zero-padded form) forbids an
encoding (RFC 9110). `Vary: accept-encoding` is set whenever compressed
variants may exist — including on identity responses. Ranged responses are
never compressed (ranges over encoded bytes are broken in practice).
Unset callbacks and limits are normalized when the listener, route, or client is
created. Server `on_http` defaults to a 404 response; client `on_http` defaults
to a no-op. Server upgrade authentication defaults to denial. Client upgrade
authentication defaults to allow.
### `fio_http_listen`
```c
fio_http_listener_s *fio_http_listen(const char *url,
fio_http_settings_s settings);
#define fio_http_listen(url, ...) \
fio_http_listen(url, (fio_http_settings_s){__VA_ARGS__})
```
Starts an HTTP / WebSocket / SSE listener on `url`.
```c
fio_http_listener_s *l = fio_http_listen("0.0.0.0:3000",
.on_http = on_request,
.log = 1);
```
The listener is rooted at `/`; a path component in the listen URL is ignored.
Use `fio_http_route` for per-prefix behavior.
### `fio_http_listener_settings`
```c
fio_http_settings_s *fio_http_listener_settings(fio_http_listener_s *l);
```
Returns the listener's root settings object. Prefer `fio_http_route` for
changing root routing behavior after the listener is created.
### `FIO_HTTP_AUTHENTICATE_ALLOW`
```c
int FIO_HTTP_AUTHENTICATE_ALLOW(fio_http_s *h);
```
Authentication callback that always allows the upgrade. Use it for
`.on_authenticate_sse` or `.on_authenticate_websocket` when no gate is needed.
### `fio_http_io` and `fio_http_settings`
```c
fio_io_s *fio_http_io(fio_http_s *h);
fio_http_settings_s *fio_http_settings(fio_http_s *h);
```
`fio_http_io` returns the IO object attached to the handle, when one exists.
`fio_http_settings` returns the route settings matching the handle's original
path, when connection data is available.
---
## Routing
### Prefix routes
```c
int fio_http_route(fio_http_listener_s *listener,
const char *url,
fio_http_settings_s settings);
#define fio_http_route(listener, url, ...) \
fio_http_route(listener, url, (fio_http_settings_s){__VA_ARGS__})
fio_http_settings_s *fio_http_route_settings(fio_http_listener_s *l,
const char *url);
```
Routes are best-prefix matches:
- `/` matches everything.
- `/user` matches `/user` and `/user/...`, but not `/user...`.
- More specific prefixes such as `/user/new` win over `/user`.
Route declaration order is not significant unless an existing route is replaced.
Route settings inherit missing listener callbacks and limits, including `udata`,
`on_finish`, `on_stop`, SSE / WebSocket authentication callbacks,
header/body/message limits, timeouts, `public_folder`, `compress_static`, and
`log`. TLS settings are ignored on routes.
Only `on_http`, `on_finish`, `udata`, the authentication callbacks,
`public_folder`, `max_age`, and `compress_static` are effective per route.
Upgraded-connection callbacks (`on_open`, `on_message`, `on_ready`,
`on_shutdown`, `on_close`), `queue`, `log`, limits, timeouts,
`compress_dynamic`, and `compress_ws` are connection-global: they are read
from the listener's root settings, so per-route values are stored but unused.
Note that `max_age` is **not** inherited — a route that serves static files
must set its own.
### Resource action helper
```c
typedef enum {
FIO_HTTP_RESOURCE_NONE,
FIO_HTTP_RESOURCE_INDEX,
FIO_HTTP_RESOURCE_SHOW,
FIO_HTTP_RESOURCE_NEW,
FIO_HTTP_RESOURCE_EDIT,
FIO_HTTP_RESOURCE_CREATE,
FIO_HTTP_RESOURCE_UPDATE,
FIO_HTTP_RESOURCE_DELETE,
FIO_HTTP_RESOURCE_QUERY,
} fio_http_resource_action_e;
fio_http_resource_action_e fio_http_resource_action(fio_http_s *h);
```
Maps the current method and routed path to a small REST-style action:
| Action | Request shape |
| --- | --- |
| `FIO_HTTP_RESOURCE_INDEX` | `GET /` |
| `FIO_HTTP_RESOURCE_SHOW` | `GET /:id` |
| `FIO_HTTP_RESOURCE_NEW` | `GET /new` |
| `FIO_HTTP_RESOURCE_EDIT` | `GET /:id/edit` |
| `FIO_HTTP_RESOURCE_CREATE` | `PUT`, `POST`, or `PATCH /` |
| `FIO_HTTP_RESOURCE_UPDATE` | `PUT`, `POST`, or `PATCH /:id` |
| `FIO_HTTP_RESOURCE_DELETE` | `DELETE /:id` |
| `FIO_HTTP_RESOURCE_QUERY` | `QUERY` (any path) |
`FIO_HTTP_RESOURCE_NONE` is returned for unsupported shapes or invalid input.
---
## Static File Service
When the matching route has a `public_folder`, the module attempts a static
file response (via `fio_http_static_file_response`) for `GET` and `HEAD`
requests **before** calling `on_http`. All other methods (`POST`, `PUT`,
`DELETE`, `OPTIONS`, ...) fall through to `on_http` untouched — a static
file is not a valid response to them (RFC 9110 §9.3) — as do misses (not
found or unsafe path), with the status pre-set to 200.
For the listener's root `public_folder` the full original path is appended
to the folder; for a route-level `public_folder` the routed (prefix-trimmed)
path is appended instead. Applications can also call
`fio_http_static_file_response` directly from `on_http` — the API is
method-agnostic (except `OPTIONS`, which it refuses), so the application
can answer any other method with a file when it chooses to.
### Path resolution and safety
- The request path is URL-decoded and joined to the root folder; paths
folding backwards (`/../`, `//`) are rejected.
- A path resolving to a folder is retried as the folder's index file
(`FIO_HTTP_DEFAULT_INDEX_FILENAME`, `"index"` by default).
- With `FIO_HTTP_STATIC_FILE_COMPLETION` (default `1`), unresolved paths are
retried with `.html`, `.htm`, `.txt`, and `.md` appended (so a folder
resolves to `index.html`, `index.htm`, ...). Only regular files and
symlinks are served.
- The response `Content-Type` comes from the MIME registry
(`fio_http_mimetype`); an unregistered extension logs a warning.
### Pre-compressed variants
Unless the request is ranged (see below), the `Accept-Encoding` header is
tested in preference order **`br` → `zstd` → `gzip` → `deflate`**, mapping
to `.br`, `.zstd`, `.gz`, and `.zip` next to the
original. The first accepted variant that exists on disk **and** is at
least as fresh as the original (modification time) is served, with
`Content-Encoding` set accordingly. Tokens forbidden with `q=0` are never
selected (RFC 9110 §12.5.3).
With `compress_static` enabled, missing or stale `.br` / `.gz` variants are
created on demand and written into the root folder. Attached handles read the
flag from the matching route's settings (routes inherit the listener's root
value at route-creation); detached handles (created manually, with no
settings) gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` handle cflag instead.
- creation is limited to compressible (text-like) MIME types and originals
between 1024 bytes and `FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT` (2 MiB);
- creation is skipped when compression would not shrink the file;
- `.br` variants are compressed at brotli quality 4 (the benchmark-reviewed
fast-path ceiling; q5+ engages the slow hash-chain path) and `.gz`
variants at gzip level 6;
- `zstd` / `deflate` variants are only ever served pre-generated — they are
never created on demand;
- on-demand creation writes into the served folder and is triggered by
client requests; the folder must be writable for creation to succeed
(a write failure disables creation, see memoization below). Variants
may also be pre-generated at deploy time with any external tool — the
server only stats the files.
`compress_static` is a failure-memoization shift register, updated atomically
(the IO threads share the listener / route settings): any non-zero value
enables on-demand creation. A compression success re-seeds bit 0
(`value |= 1`); any other failure shifts the value left (`value <<= 1`), so
8 consecutive failures shift out of the `uint8_t` and disable creation, as
does a single filesystem-full / permission error (`ENOSPC`, `EACCES`,
`EROFS`, `EDQUOT` — `value = 0`). Once 0, creation stays disabled until the
settings are re-applied. Detached handles carry no memoization state.
`Vary: accept-encoding` is set whenever a variant is served, and on
identity responses whenever variants may be created, so caches key on the
client's `Accept-Encoding`.
### Conditional, ranged, and HEAD requests
- The `ETag` is a hash of the served file's `stat` data (it changes when
the file changes). A matching `If-None-Match` (GET/HEAD) yields a `304`.
- `Last-Modified` reflects the file's modification time;
`Cache-Control: max-age=` is only sent when `max_age` is
non-zero.
- Single `Range: bytes=start-end` requests are supported (`206` with
`Content-Range`), including open-ended (`bytes=N-`) and suffix
(`bytes=-N`) forms. `If-Range` must equal the current `ETag` or the
range is ignored. Unsatisfiable ranges get `416` with
`Content-Range: bytes */`. Ranged responses are always identity
(no variant selection) and carry no `ETag`. `Accept-Ranges: bytes` is
always sent.
- `HEAD` requests pass through the same logic and finish with headers only.
---
## HTTP Client Connections
```c
fio_io_s *fio_http_connect(const char *url,
fio_http_s *h,
fio_http_settings_s settings);
#define fio_http_connect(url, h, ...) \
fio_http_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects as an HTTP / WebSocket / SSE client. If `h` is `NULL`, a new handle is
created. Missing method, path, query, and `Host` are filled from `url`.
Recognized upgrade schemes:
- `ws://` and `wss://` call `fio_http_websocket_set_request`.
- `sse://` and `sses://` call `fio_http_sse_set_request`.
Client `on_http` receives the response handle. If the response accepts a
WebSocket or SSE upgrade, the connection switches to the upgraded protocol and
uses the upgraded callbacks instead.
Interim `1xx` responses (i.e., `100 Continue`, `103 Early Hints`) other than
`101` are skipped (RFC 9110 §15.2): `on_http` runs once, for the final
response, and interim headers are discarded. Responses to `HEAD` requests, and
`204` / `304` responses, never read a body, even if they carry
`content-length` or `transfer-encoding` headers. Those headers stay visible
as response headers.
### `fio_http_websocket_connect`
```c
fio_io_s *fio_http_websocket_connect(const char *url,
fio_http_s *h,
fio_http_settings_s settings);
#define fio_http_websocket_connect(url, h, ...) \
fio_http_websocket_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to a WebSocket server. A convenience wrapper around
`fio_http_connect` that normalizes the `url` scheme before connecting:
`http://` becomes `ws://`, `https://` becomes `wss://`, and a missing scheme
defaults to `ws://` (`ws://` / `wss://` and any other scheme pass through
unchanged).
`fio_http_connect` issues the WebSocket upgrade request automatically for
`ws://` / `wss://` URLs. If the server accepts the upgrade (`101`), the
connection switches to the WebSocket protocol and the `on_open` /
`on_message` / `on_close` callbacks are used. Any other response is routed
to `settings.on_http` with the response handle.
---
## WebSocket and SSE Upgrades
### Upgrade tests and request / response setup
These helpers are declared by the HTTP handle and used by the HTTP module:
```c
int fio_http_websocket_requested(fio_http_s *h);
int fio_http_websocket_accepted(fio_http_s *h);
void fio_http_upgrade_websocket(fio_http_s *h);
void fio_http_websocket_set_request(fio_http_s *h);
int fio_http_sse_requested(fio_http_s *h);
int fio_http_sse_accepted(fio_http_s *h);
void fio_http_upgrade_sse(fio_http_s *h);
void fio_http_sse_set_request(fio_http_s *h);
```
`*_requested` inspects request headers. `*_accepted` inspects client-side
response state and marks the handle as upgraded on success. `fio_http_upgrade_*`
sets the server response state for the upgrade. `*_set_request` prepares a
client request handle.
Server listeners normally call the authentication callbacks and then upgrade
automatically when a request asks for WebSocket or SSE.
### WebSocket write and callback override
```c
int fio_http_websocket_write(fio_http_s *h,
const void *buf,
size_t len,
uint8_t is_text);
int fio_http_on_message_set(fio_http_s *h,
void (*on_message)(fio_http_s *,
fio_buf_info_s,
uint8_t));
```
`fio_http_websocket_write` writes one WebSocket message and fails if the handle
is not an established WebSocket. `is_text` selects text vs. binary.
**Serialization:** `fio_http_websocket_write` must be serialized per
connection — concurrent calls on the same handle (e.g. from pub/sub or queue
callbacks racing on multiple threads) may interleave frame bytes and must be
guarded by the caller (mutex, or route all writes through the connection's
queue). This also protects the per-connection compression context.
`fio_http_on_message_set` overrides the `on_message` callback for the current
WebSocket connection. Passing `NULL` restores the settings callback. It returns
`-1` when the handle is not ready for this change.
Incoming WebSocket messages are accumulated up to `ws_max_msg_size` and then
scheduled on the HTTP queue. Control frames are handled by the module.
### SSE write
```c
typedef struct {
fio_buf_info_s id;
fio_buf_info_s event;
fio_buf_info_s data;
} fio_http_sse_write_args_s;
int fio_http_sse_write(fio_http_s *h, fio_http_sse_write_args_s args);
#define fio_http_sse_write(h, ...) \
fio_http_sse_write((h), ((fio_http_sse_write_args_s){__VA_ARGS__}))
```
Writes one UTF-8 SSE event. `data` is required. `id` and `event` are optional.
Multiline data is emitted as repeated `data:` lines. The helper fails if the
handle is not an established SSE connection.
### Pub/sub helpers
```c
#define fio_http_subscribe(h, ...) \
fio_pubsub_subscribe(.io = fio_http_io(h), __VA_ARGS__)
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT(fio_pubsub_msg_s *msg);
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT_TEXT(fio_pubsub_msg_s *msg);
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT_BINARY(fio_pubsub_msg_s *msg);
void FIO_HTTP_SSE_SUBSCRIBE_DIRECT(fio_pubsub_msg_s *msg);
```
`fio_http_subscribe` subscribes using the handle's IO object.
The direct WebSocket callbacks write published messages to the WebSocket. The
plain `FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT` helper checks short payloads for
UTF-8 and selects text or binary; the explicit `_TEXT` and `_BINARY` variants
skip that guess.
`FIO_HTTP_SSE_SUBSCRIBE_DIRECT` writes an SSE event whose event name is the
channel and whose data is the published message.
---
## HTTP Handle API Pulled In by `FIO_HTTP`
The HTTP handle is the request / response object exposed to callbacks.
### Lifetime
```c
typedef struct fio_http_s fio_http_s;
fio_http_s *fio_http_new(void);
fio_http_s *fio_http_new_copy_request(fio_http_s *old);
void fio_http_free(fio_http_s *h);
fio_http_s *fio_http_dup(fio_http_s *h);
fio_http_s *fio_http_destroy(fio_http_s *h);
void fio_http_start_time_set(fio_http_s *h);
fio_http_s *fio_http_clear_response(fio_http_s *h, bool clear_body);
void fio_http_close(fio_http_s *h);
```
`fio_http_s` is reference-counted. It is not designed as a thread-safe mutable
object. `fio_http_close` closes the associated connection, including an ordinary
HTTP/1 connection. Schedule the complete response before calling it; previously
scheduled output drains before closure.
### User data and controller data
```c
void *fio_http_udata(fio_http_s *h);
void *fio_http_udata_set(fio_http_s *h, void *udata);
void *fio_http_udata2(fio_http_s *h);
void *fio_http_udata2_set(fio_http_s *h, void *udata);
void *fio_http_cdata(fio_http_s *h);
void *fio_http_cdata_set(fio_http_s *h, void *cdata);
fio_http_controller_s *fio_http_controller(fio_http_s *h);
fio_http_controller_s *fio_http_controller_set(fio_http_s *h,
fio_http_controller_s *c);
```
`udata` and `udata2` are for application state. `cdata` and the controller are
used by the HTTP layer and custom controllers.
### Request / response properties
```c
size_t fio_http_status(fio_http_s *h);
size_t fio_http_status_set(fio_http_s *h, size_t status);
fio_str_info_s fio_http_method(fio_http_s *h);
fio_str_info_s fio_http_method_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_opath(fio_http_s *h);
fio_str_info_s fio_http_opath_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_path(fio_http_s *h);
fio_str_info_s fio_http_path_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_query(fio_http_s *h);
fio_str_info_s fio_http_query_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_version(fio_http_s *h);
fio_str_info_s fio_http_version_set(fio_http_s *h, fio_str_info_s value);
int64_t fio_http_received_at(fio_http_s *h);
int64_t fio_http_received_at_set(fio_http_s *h, int64_t value);
```
`opath` is the original path before route prefix trimming. `path` is the routed
path visible to the current callback.
`fio_http_status_set(h, 0)` normalizes to `200`; values above the internal range
normalize to `500`.
### State checks and flags
```c
int fio_http_is_clean(fio_http_s *h);
int fio_http_is_finished(fio_http_s *h);
int fio_http_is_streaming(fio_http_s *h);
int fio_http_is_upgraded(fio_http_s *h);
int fio_http_is_websocket(fio_http_s *h);
int fio_http_is_sse(fio_http_s *h);
int fio_http_is_freeing(fio_http_s *h);
```
The handle also exposes inline flag helpers generated for `cflags` and
`uflags`:
```c
uint16_t fio_http_cflags(fio_http_s *h);
uint16_t fio_http_cflags_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_unset(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_flip(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_is_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags(fio_http_s *h);
uint16_t fio_http_uflags_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_unset(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_flip(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_is_set(fio_http_s *h, uint16_t flags);
```
Prefer the state helpers unless implementing protocol or controller glue.
### Headers
```c
fio_str_info_s fio_http_request_header(fio_http_s *h,
fio_str_info_s name,
size_t index);
size_t fio_http_request_header_count(fio_http_s *h, fio_str_info_s name);
fio_str_info_s fio_http_request_header_set(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
fio_str_info_s fio_http_request_header_set_if_missing(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
fio_str_info_s fio_http_request_header_add(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
size_t fio_http_request_header_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s name,
fio_str_info_s value,
void *udata),
void *udata);
```
Response headers use the same shape with `response` in the function name:
```c
fio_http_response_header;
fio_http_response_header_count;
fio_http_response_header_set;
fio_http_response_header_set_if_missing;
fio_http_response_header_add;
fio_http_response_header_each;
```
`*_header` returns an empty value if the requested value is missing. Use
`index` for repeated values. If `name.buf == NULL`, `*_header_count` returns
the number of unique header names.
Header insertion rejects names longer than 4095 bytes and names containing the
parser's forbidden name bytes (including whitespace, colon, NUL, CR, and LF).
Values containing NUL, CR, or LF are rejected. Invalid insertion returns an empty
value without replacing an existing header. With
`FIO_HTTP_ENFORCE_LOWERCASE_HEADERS`, names are validated and copied to ASCII
lowercase in one pass; caller-owned input is not modified.
Response headers cannot be changed after the response headers are sent.
### Header value and property iteration
```c
FIO_HTTP_HEADER_EACH_VALUE(h, 1, FIO_STR_INFO1("accept"), item) {
/* item.name and item.value are length-delimited fio_buf_info_s slices. */
FIO_HTTP_HEADER_EACH_PROPERTY(item, prop) {
/* prop.name and prop.value are length-delimited slices. */
}
}
```
The macros iterate repeated fields, comma-separated values and semicolon-separated
properties without copying. They trim surrounding spaces and tabs; quoted
strings remain quoted, including escapes. Slices borrow header storage and are
not necessarily NUL-terminated: use their `.len` rather than string functions.
Do not change headers during iteration. `value.buf == NULL` marks the end;
empty property values may have a non-NULL buffer and zero length. The helpers
`fio_http_header_each_first` / `fio_http_header_each_next` and
`fio_http_header_each_property_first` / `fio_http_header_each_property_next`
can also be used directly.
### Body storage and reads
```c
size_t fio_http_body_length(fio_http_s *h);
size_t fio_http_body_pos(fio_http_s *h);
size_t fio_http_body_seek(fio_http_s *h, ssize_t pos);
fio_str_info_s fio_http_body_read(fio_http_s *h, size_t length);
fio_str_info_s fio_http_body_read_until(fio_http_s *h,
char token,
size_t limit);
void fio_http_body_expect(fio_http_s *h, size_t expected_length);
void fio_http_body_write(fio_http_s *h, const void *data, size_t len);
int fio_http_body_fd(fio_http_s *h);
void fio_http_body_close(fio_http_s *h);
```
The handle stores body data in RAM until the configured threshold is crossed,
then uses a temporary file. `fio_http_body_fd` returns that file descriptor or
`-1`.
`fio_http_body_close` releases the body's RAM buffer and temporary file (if
any) and resets the body to empty. The body is also released automatically
when the handle is cleared or destroyed, so calling this is only needed to
free resources early, once the body has been consumed.
### Body parsing
```c
typedef struct fio_http_body_parse_callbacks_s fio_http_body_parse_callbacks_s;
typedef struct fio_http_body_parse_result_s fio_http_body_parse_result_s;
fio_http_body_parse_result_s fio_http_body_parse(
fio_http_s *h,
const fio_http_body_parse_callbacks_s *callbacks,
void *udata);
```
The body parser auto-detects JSON, URL-encoded, and multipart/form-data input.
Callbacks build application objects for primitives, arrays, maps, and file
uploads.
`fio_http_body_parse_callbacks_s` contains primitive callbacks (`on_null`,
`on_true`, `on_false`, `on_number`, `on_float`, `on_string`), container
callbacks (`on_array`, `on_map`, `array_push`, `map_set`, `array_done`,
`map_done`), multipart callbacks (`on_file`, `on_file_data`, `on_file_done`),
and error / cleanup callbacks (`on_error`, `free_unused`).
`fio_http_body_parse_result_s` reports the top-level result, consumed bytes,
and error code.
### Cookies
```c
typedef enum fio_http_cookie_same_site_e {
FIO_HTTP_COOKIE_SAME_SITE_BROWSER_DEFAULT = 0,
FIO_HTTP_COOKIE_SAME_SITE_NONE,
FIO_HTTP_COOKIE_SAME_SITE_LAX,
FIO_HTTP_COOKIE_SAME_SITE_STRICT,
} fio_http_cookie_same_site_e;
typedef struct fio_http_cookie_args_s fio_http_cookie_args_s;
int fio_http_cookie_set(fio_http_s *h, fio_http_cookie_args_s args);
#define fio_http_cookie_set(h, ...) \
fio_http_cookie_set((h), (fio_http_cookie_args_s){__VA_ARGS__})
fio_str_info_s fio_http_cookie(fio_http_s *h,
const char *name,
size_t name_len);
size_t fio_http_cookie_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s name,
fio_str_info_s value,
void *udata),
void *udata);
size_t fio_http_set_cookie_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s set_cookie,
fio_str_info_s value,
void *udata),
void *udata);
```
`fio_http_cookie_args_s` contains `name`, `value`, optional `domain` and `path`,
`max_age`, `same_site`, and the `secure`, `http_only`, and `partitioned` flags.
An empty / missing value deletes the cookie by setting a negative max-age.
### Writing responses
```c
typedef struct fio_http_write_args_s {
const void *buf;
size_t len;
size_t offset;
int fd;
void (*dealloc)(void *);
int copy;
int finish;
} fio_http_write_args_s;
void fio_http_write(fio_http_s *h, fio_http_write_args_s args);
#define fio_http_write(h, ...) fio_http_write(h, (fio_http_write_args_s){__VA_ARGS__})
#define fio_http_finish(h) fio_http_write(h, .finish = 1)
```
`fio_http_write` sends headers on the first write and then writes body data. If
`finish` is set, the response is complete. Without `finish`, the response is a
stream. A finished response without an explicit `content-length` header gets
one automatically. The response starts with the first write that carries body
data (a buffer or a file) or `finish`: an empty write without `finish` is a
no-op, so headers can still be set, and a response that is finished without
any body data is sent with `content-length: 0` rather than as an empty
chunked stream. HTTP/1.1 streams use `transfer-encoding: chunked`; HTTP/1.0 responses
never use a transfer coding (RFC 9112 §6.1), so an HTTP/1.0 stream is sent
unframed and delimited by closing the connection, even if `keep-alive` was
requested.
Responses without content (`1xx`, `204`, `205`, `304`, RFC 9110 §15) never
send body data, are never streamed or compressed, and never send
`transfer-encoding` (an application-provided value is removed). Their
`content-length` follows RFC 9110 §8.6:
- `1xx` / `204`: removed, never added.
- `205`: always `content-length: 0` (§15.3.6), replacing any other value.
- `304`: an application-provided value is kept (it describes the selected
representation); none is added automatically.
Responses to `HEAD` requests send the headers a `GET` would (a finished
response still gets its automatic `content-length`), but never send content
(RFC 9110 §9.3.2): body writes are discarded and the response is never
streamed (no `transfer-encoding: chunked`). File writes use `fd` and `offset`; the file descriptor is always closed
by the write path.
On upgraded handles, `fio_http_write` routes through the WebSocket or SSE
controller. For WebSocket text/binary choice, call `fio_http_websocket_write`
directly when the payload type matters.
### General helpers
```c
int fio_http_send_error_response(fio_http_s *h, size_t status);
int fio_http_etag_is_match(fio_http_s *h);
int fio_http_static_file_response(fio_http_s *h,
fio_str_info_s root_folder,
fio_str_info_s file_name,
size_t max_age);
fio_str_info_s fio_http_status2str(size_t status);
void fio_http_write_log(fio_http_s *h);
int fio_http_from(fio_str_info_s *dest, const fio_http_s *h);
fio_str_info_s fio_http_date(uint64_t now_in_seconds);
fio_str_info_s fio_http_log_time(uint64_t now_in_seconds);
int64_t fio_http_get_timestump(void);
```
`fio_http_get_timestump` is the inline timestamp helper used by the handle for
`received_at` and log timing. User code usually reads timestamps with
`fio_http_received_at` or formats them with `fio_http_date` /
`fio_http_log_time`.
`fio_http_from` writes a best-effort peer address starting with the `Forwarded`
header, then socket peer address, then `"[unknown]"`.
`fio_http_static_file_response` sends a file from a root folder and finishes
the response on success (0), returning -1 when the application should handle
the request itself. See *Static File Service* for the path resolution,
pre-compressed variant, range, and caching semantics.
### Path and MIME helpers
```c
#define FIO_HTTP_PATH_EACH(path, pos)
int fio_http_mimetype_register(char *file_ext,
size_t file_ext_len,
fio_str_info_s mime_type);
fio_str_info_s fio_http_mimetype(char *file_ext, size_t file_ext_len);
```
`FIO_HTTP_PATH_EACH` iterates decoded path sections using a 4096-byte stack
buffer. The MIME registry is not thread-safe.
### Controller
```c
typedef struct fio_http_controller_s fio_http_controller_s;
struct fio_http_controller_s {
uintptr_t private_flags;
void (*on_destroyed)(fio_http_s *h);
void (*send_headers)(fio_http_s *h);
void (*write_body)(fio_http_s *h, fio_http_write_args_s args);
void (*on_finish)(fio_http_s *h);
void (*close_io)(fio_http_s *h);
int (*get_fd)(fio_http_s *h);
};
```
Controllers allow the handle to write through different protocols. Initialize
custom controllers to zero before use. If controller callbacks are not
thread-safe, call `fio_http_write` only from the controller's expected thread.
---
## Compile-Time Knobs
HTTP module defaults:
```c
FIO_HTTP_DEFAULT_MAX_HEADER_SIZE /* 32768 */
FIO_HTTP_DEFAULT_MAX_LINE_LEN /* 8192 */
FIO_HTTP_DEFAULT_MAX_BODY_SIZE /* 33554432 */
FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE /* 262144 */
FIO_HTTP_DEFAULT_TIMEOUT /* 50 */
FIO_HTTP_DEFAULT_TIMEOUT_LONG /* 50 */
FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER /* 0 */
FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT
FIO_WEBSOCKET_STATS /* 0 */
FIO_HTTP_WEBSOCKET_DEFLATE_MIN /* 1024 */
```
HTTP handle defaults:
```c
FIO_HTTP_EXACT_LOGGING /* fuzzy timestamps by default with IO */
FIO_HTTP_BODY_RAM_LIMIT /* 1 << 17 */
FIO_HTTP_CACHE_LIMIT /* 0 */
FIO_HTTP_CACHE_STR_MAX_LEN /* 1 << 12 */
FIO_HTTP_CACHE_USES_MUTEX /* 1 */
FIO_HTTP_PRE_CACHE_KNOWN_HEADERS /* 1 */
FIO_HTTP_DEFAULT_INDEX_FILENAME /* "index" */
FIO_HTTP_STATIC_FILE_COMPLETION /* 1 */
FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT /* 1 << 21 */
FIO_HTTP_LOG_X_REQUEST_START /* 1 */
FIO_HTTP_ENFORCE_LOWERCASE_HEADERS /* 0 */
```
State and controller flag macros are exposed for protocol glue:
```c
FIO_HTTP_STATE_STREAMING
FIO_HTTP_STATE_FINISHED
FIO_HTTP_STATE_UPGRADED
FIO_HTTP_STATE_WEBSOCKET
FIO_HTTP_STATE_SSE
FIO_HTTP_STATE_COOKIES_PARSED
FIO_HTTP_STATE_FREEING
FIO_HTTP_CFLAG_COMPRESS_DYNAMIC
FIO_HTTP_CFLAG_COMPRESS_WS
FIO_HTTP_CFLAG_COMPRESS_STATIC
```
Prefer public helper functions over reading state bits directly in application
code.
---
## Minimal Server
```c
#define FIO_HTTP
#include FIO_INCLUDE_FILE
static void on_http(fio_http_s *h) {
fio_http_response_header_set(h,
FIO_STR_INFO1("content-type"),
FIO_STR_INFO1("text/plain"));
fio_http_write(h, .buf = "hello\n", .len = 6, .finish = 1);
}
void start_http(void) {
fio_http_listen("0.0.0.0:3000", .on_http = on_http, .log = 1);
}
```
For WebSocket or SSE routes, provide the relevant authentication and upgraded
connection callbacks in the route settings.
## Hash Function Testing
During development I tested the Hash functions using [the SMHasher testing suite (@rurban's fork)](https://github.com/rurban/smhasher). The testing suite is often growing with more tests, so I do not know what the future may bring... but attached are the test results for Risky Hash, Stable Hash (64 and 128 bit variation) as they were at the time of (re)development (March, 2023).
### Risky Hash SMHasher Results
The following results were achieved on my personal computer when testing the facil.io Risky Hash (`fio_risky_hash`).
```txt
------------------------------------------------------------
--- Testing Risky "facil.io Risky Hash" GOOD
[[[ Sanity Tests ]]]
Verification value 0x407D2C05 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 17.861 bytes/cycle - 51100.77 MiB/sec @ 3 ghz
Alignment 6 - 17.833 bytes/cycle - 51019.28 MiB/sec @ 3 ghz
Alignment 5 - 17.832 bytes/cycle - 51018.87 MiB/sec @ 3 ghz
Alignment 4 - 17.853 bytes/cycle - 51077.60 MiB/sec @ 3 ghz
Alignment 3 - 17.844 bytes/cycle - 51051.63 MiB/sec @ 3 ghz
Alignment 2 - 17.854 bytes/cycle - 51080.37 MiB/sec @ 3 ghz
Alignment 1 - 17.912 bytes/cycle - 51245.99 MiB/sec @ 3 ghz
Alignment 0 - 19.064 bytes/cycle - 54541.55 MiB/sec @ 3 ghz
Average - 18.006 bytes/cycle - 51517.01 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 13.90 cycles/hash
Small key speed test - 2-byte keys - 14.59 cycles/hash
Small key speed test - 3-byte keys - 14.27 cycles/hash
Small key speed test - 4-byte keys - 14.32 cycles/hash
Small key speed test - 5-byte keys - 14.81 cycles/hash
Small key speed test - 6-byte keys - 14.99 cycles/hash
Small key speed test - 7-byte keys - 15.00 cycles/hash
Small key speed test - 8-byte keys - 14.00 cycles/hash
Small key speed test - 9-byte keys - 14.15 cycles/hash
Small key speed test - 10-byte keys - 14.00 cycles/hash
Small key speed test - 11-byte keys - 14.00 cycles/hash
Small key speed test - 12-byte keys - 14.00 cycles/hash
Small key speed test - 13-byte keys - 14.00 cycles/hash
Small key speed test - 14-byte keys - 14.11 cycles/hash
Small key speed test - 15-byte keys - 14.00 cycles/hash
Small key speed test - 16-byte keys - 14.72 cycles/hash
Small key speed test - 17-byte keys - 15.74 cycles/hash
Small key speed test - 18-byte keys - 15.62 cycles/hash
Small key speed test - 19-byte keys - 15.60 cycles/hash
Small key speed test - 20-byte keys - 15.66 cycles/hash
Small key speed test - 21-byte keys - 15.83 cycles/hash
Small key speed test - 22-byte keys - 15.69 cycles/hash
Small key speed test - 23-byte keys - 15.73 cycles/hash
Small key speed test - 24-byte keys - 15.63 cycles/hash
Small key speed test - 25-byte keys - 15.67 cycles/hash
Small key speed test - 26-byte keys - 15.66 cycles/hash
Small key speed test - 27-byte keys - 15.76 cycles/hash
Small key speed test - 28-byte keys - 15.67 cycles/hash
Small key speed test - 29-byte keys - 15.68 cycles/hash
Small key speed test - 30-byte keys - 15.65 cycles/hash
Small key speed test - 31-byte keys - 15.64 cycles/hash
Average 14.971 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 148.866 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 85.492 cycles/op (1.0 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 158.746 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 74.728 cycles/op (2.5 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 62.909 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 32.332 cycles/op (0.2 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.585333%
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.695333%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.619333%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.662667%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.706000%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.693333%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.730667%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.706667%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.672000%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.786667%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.621333%
Testing 160-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.710000%
Testing 512-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.835333%
Testing 1024-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.800000%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 19 bits: 2358/2368 (1.00x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 24 bits: 90/76 (1.18x)
Testing distribution - Worst bias is the 13-bit window at bit 41 - 0.600%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 179 (0.95x)
Testing collisions (high 24-35 bits) - Worst is 25 bits: 23889/23794 (1.00x)
Testing collisions (low 32-bit) - Expected 188.2, actual 215 (1.14x) (27)
Testing collisions (low 24-35 bits) - Worst is 33 bits: 108/94 (1.15x)
Testing distribution - Worst bias is the 17-bit window at bit 16 - 0.075%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2390 (1.01x) (18)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 602/593 (1.01x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2313 (0.98x)
Testing collisions (low 25-38 bits) - Worst is 35 bits: 301/296 (1.01x)
Testing distribution - Worst bias is the 19-bit window at bit 15 - 0.051%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2509 (1.02x) (49)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 42/38 (1.09x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2449 (1.00x) (-11)
Testing collisions (low 25-38 bits) - Worst is 35 bits: 316/307 (1.03x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.058%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23532 (1.00x) (95)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 28/22 (1.22x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23333 (1.00x) (-104)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 96/91 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 16 - 0.020%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2026 (0.98x)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 518/517 (1.00x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2139 (1.03x) (71)
Testing collisions (low 25-38 bits) - Worst is 36 bits: 142/129 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 8 - 0.043%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8072 (1.01x) (51)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 37/31 (1.18x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8035 (1.00x) (14)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 32/31 (1.02x)
Testing distribution - Worst bias is the 20-bit window at bit 37 - 0.039%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26268 (0.99x) (-183)
Testing collisions (high 27-42 bits) - Worst is 39 bits: 225/206 (1.09x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26552 (1.00x) (101)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3381/3309 (1.02x)
Testing distribution - Worst bias is the 20-bit window at bit 36 - 0.027%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1392 (0.99x) (-8)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 93/87 (1.06x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1402 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 48/43 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 11 - 0.051%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84403 (1.00x) (-143)
Testing collisions (high 28-44 bits) - Worst is 34 bits: 21303/21169 (1.01x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84679 (1.00x) (133)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 45/41 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.014%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 930 (1.02x) (20)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 71/56 (1.25x)
Testing collisions (low 32-bit) - Expected 910.2, actual 922 (1.01x) (12)
Testing collisions (low 25-37 bits) - Worst is 35 bits: 121/113 (1.06x)
Testing distribution - Worst bias is the 19-bit window at bit 52 - 0.094%
Keyset 'Sparse' - 512-bit keys with up to 3 bits set - 22370049 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 58155.4, actual 58001 (1.00x) (-154)
Testing collisions (high 28-43 bits) - Worst is 42 bits: 69/56 (1.21x)
Testing collisions (low 32-bit) - Expected 58155.4, actual 58151 (1.00x) (-4)
Testing collisions (low 28-43 bits) - Worst is 41 bits: 121/113 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.015%
Keyset 'Sparse' - 1024-bit keys with up to 2 bits set - 524801 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32.1, actual 37 (1.15x) (5)
Testing collisions (high 22-32 bits) - Worst is 31 bits: 75/64 (1.17x)
Testing collisions (low 32-bit) - Expected 32.1, actual 40 (1.25x) (8)
Testing collisions (low 22-32 bits) - Worst is 32 bits: 40/32 (1.25x)
Testing distribution - Worst bias is the 16-bit window at bit 17 - 0.136%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 512.4, actual 551 (1.08x) (39)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1105/1024 (1.08x)
Testing collisions (low 32-bit) - Expected 512.4, actual 526 (1.03x) (14)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/64 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.103%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 670 (1.00x) (2)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 28/20 (1.34x)
Testing collisions (low 32-bit) - Expected 668.6, actual 675 (1.01x) (7)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 350/334 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 1 - 0.086%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 620 (0.93x)
Testing collisions (high 24-37 bits) - Worst is 36 bits: 45/41 (1.08x)
Testing collisions (low 32-bit) - Expected 668.6, actual 634 (0.95x)
Testing collisions (low 24-37 bits) - Worst is 28 bits: 10778/10667 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.053%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17547 (1.01x) (225)
Testing collisions (high 27-41 bits) - Worst is 38 bits: 283/270 (1.04x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17096 (0.99x) (-226)
Testing collisions (low 27-41 bits) - Worst is 41 bits: 37/33 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.035%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8170 (1.00x) (-16)
Testing collisions (high 26-40 bits) - Worst is 33 bits: 4129/4094 (1.01x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8208 (1.00x) (22)
Testing collisions (low 26-40 bits) - Worst is 33 bits: 4128/4094 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 21 - 0.041%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8179 (1.00x) (-7)
Testing collisions (high 26-40 bits) - Worst is 31 bits: 16584/16362 (1.01x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8243 (1.01x) (57)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.042%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8296 (1.01x) (110)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 74/63 (1.16x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8168 (1.00x) (-18)
Testing collisions (low 26-40 bits) - Worst is 38 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.035%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8186 (1.00x)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 72/63 (1.13x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8238 (1.01x) (52)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1063/1023 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 63 - 0.031%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8212 (1.00x) (26)
Testing collisions (high 26-40 bits) - Worst is 34 bits: 2056/2047 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8083 (0.99x) (-103)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 259/255 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.037%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8052 (0.98x) (-134)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8267 (1.01x) (81)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 2 - 0.045%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8136 (0.99x) (-50)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 130/127 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8138 (0.99x) (-48)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 58 - 0.044%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 7961 (0.97x)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8088 (0.99x) (-98)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1038/1023 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 24 - 0.047%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8054 (0.98x) (-132)
Testing collisions (high 26-40 bits) - Worst is 26 bits: 502359/503108 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8309 (1.01x) (123)
Testing collisions (low 26-40 bits) - Worst is 34 bits: 2101/2047 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 14 - 0.052%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8247 (1.01x) (61)
Testing collisions (high 26-40 bits) - Worst is 33 bits: 4159/4094 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8217 (1.00x) (31)
Testing collisions (low 26-40 bits) - Worst is 39 bits: 71/63 (1.11x)
Testing distribution - Worst bias is the 20-bit window at bit 31 - 0.052%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8076 (0.99x) (-110)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 150/127 (1.17x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8278 (1.01x) (92)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1069/1023 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 36 - 0.041%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8108 (0.99x) (-78)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8100 (0.99x) (-86)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 295/255 (1.15x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.049%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 30 bits: 499/465 (1.07x)
Testing collisions (low 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (low 23-34 bits) - Worst is 25 bits: 14836/14754 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 24 - 0.118%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 96 (0.82x)
Testing collisions (high 23-34 bits) - Worst is 26 bits: 7398/7413 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (low 23-34 bits) - Worst is 25 bits: 14917/14754 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 46 - 0.104%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (high 23-34 bits) - Worst is 29 bits: 934/930 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 35/29 (1.20x)
Testing distribution - Worst bias is the 17-bit window at bit 39 - 0.070%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 112 (0.96x)
Testing collisions (high 23-34 bits) - Worst is 27 bits: 3790/3716 (1.02x)
Testing collisions (low 32-bit) - Expected 116.4, actual 140 (1.20x) (24)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 39 - 0.112%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 30 bits: 483/465 (1.04x)
Testing distribution - Worst bias is the 17-bit window at bit 52 - 0.132%
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 132 (1.13x) (16)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing collisions (low 32-bit) - Expected 116.4, actual 100 (0.86x)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 947/930 (1.02x)
Testing distribution - Worst bias is the 17-bit window at bit 15 - 0.109%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 38 (0.77x)
Testing collisions (high 23-33 bits) - Worst is 23 bits: 24681/24735 (1.00x)
Testing collisions (low 32-bit) - Expected 49.6, actual 44 (0.89x)
Testing collisions (low 23-33 bits) - Worst is 31 bits: 108/99 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 22 - 0.120%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3447 (0.99x) (-36)
Testing collisions (high 26-39 bits) - Worst is 26 bits: 217954/217072 (1.00x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3344 (0.96x)
Testing collisions (low 26-39 bits) - Worst is 38 bits: 57/54 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 10 - 0.087%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 40269 (1.00x) (-20)
Testing collisions (high 27-42 bits) - Worst is 38 bits: 644/630 (1.02x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40110 (1.00x) (-179)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 41/39 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.024%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 227182.3, actual 227638 (1.00x) (456)
Testing collisions (high 29-45 bits) - Worst is 39 bits: 1794/1780 (1.01x)
Testing collisions (low 32-bit) - Expected 227182.3, actual 226966 (1.00x) (-216)
Testing collisions (low 29-45 bits) - Worst is 42 bits: 253/222 (1.14x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.009%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 865959.1, actual 866176 (1.00x) (217)
Testing collisions (high 30-47 bits) - Worst is 43 bits: 437/425 (1.03x)
Testing collisions (low 32-bit) - Expected 865959.1, actual 865959 (1.00x)
Testing collisions (low 30-47 bits) - Worst is 45 bits: 121/106 (1.14x)
Testing distribution - Worst bias is the 20-bit window at bit 45 - 0.004%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25652 (1.01x) (263)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 31/24 (1.25x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25380 (1.00x) (-9)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 412/397 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 12 - 0.022%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25507 (1.00x) (118)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 29/24 (1.17x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25496 (1.00x) (107)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 204/198 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 61 - 0.021%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25559 (1.01x) (170)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 60/49 (1.21x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25471 (1.00x) (82)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 35/24 (1.41x)
Testing distribution - Worst bias is the 20-bit window at bit 63 - 0.033%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1822 (0.98x)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 30/29 (1.03x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1837 (0.99x) (-25)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 229258/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 42 - 0.071%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1827 (0.98x) (-35)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 131/116 (1.13x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1832 (0.98x) (-30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 32/29 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 24 - 0.069%
Keyset 'Words' - 466569 dict words
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 27 (1.07x) (2)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 27/25 (1.07x)
Testing collisions (low 32-bit) - Expected 25.3, actual 26 (1.03x) (1)
Testing collisions (low 22-32 bits) - Worst is 30 bits: 120/101 (1.18x)
Testing distribution - Worst bias is the 16-bit window at bit 48 - 0.155%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (high 21-29 bits) - Worst is 28 bits: 83/78 (1.06x)
Testing collisions (low 32-bit) - Expected 4.9, actual 2 (0.41x)
Testing collisions (low 21-29 bits) - Worst is 27 bits: 169/156 (1.08x)
Testing distribution - Worst bias is the 15-bit window at bit 31 - 0.261%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2808 (0.97x)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 93/90 (1.02x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 3032 (1.04x) (123)
Testing collisions (low 26-39 bits) - Worst is 38 bits: 64/45 (1.41x)
Testing distribution - Worst bias is the 19-bit window at bit 63 - 0.044%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32777 (1.00x) (52)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32511 (0.99x) (-214)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 36/31 (1.13x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1087 (0.97x)
Testing collisions (high 25-37 bits) - Worst is 27 bits: 35400/35452 (1.00x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1067 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 34 bits: 280/279 (1.00x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 3 - 0.118%
Testing bit 1
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64220/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 60 - 0.077%
Testing bit 2
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.078%
Testing bit 3
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1083/1023 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 470 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2093/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.069%
Testing bit 4
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8315/8170 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.081%
Testing bit 5
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 40 - 0.116%
Testing bit 6
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 14 - 0.106%
Testing bit 7
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.095%
Testing bit 8
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 513/511 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.075%
Testing bit 9
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.113%
Testing bit 10
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32540/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 40 - 0.099%
Testing bit 11
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32534/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.059%
Testing bit 12
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 46/31 (1.44x)
Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.058%
Testing bit 13
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8316/8170 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.054%
Testing bit 14
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 43 - 0.088%
Testing bit 15
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 281/255 (1.10x)
Testing collisions (low 32-bit) - Expected 511.9, actual 555 (1.08x) (44)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.079%
Testing bit 16
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2084/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 520/511 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 58 - 0.076%
Testing bit 17
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing distribution - Worst bias is the 17-bit window at bit 54 - 0.068%
Testing bit 18
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2114/2046 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.083%
Testing bit 19
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 27 bits: 16419/16298 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.080%
Testing bit 20
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1040/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.092%
Testing bit 21
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1062/1023 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 34 - 0.079%
Testing bit 22
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 466 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 47 - 0.077%
Testing bit 23
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32370/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.064%
Testing bit 24
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 272/255 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 27 bits: 16412/16298 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.057%
Testing bit 25
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 549/511 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.089%
Testing bit 26
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1036/1023 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 481 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 1 - 0.065%
Testing bit 27
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing collisions (low 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 13 - 0.101%
Testing bit 28
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64180/64191 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 536 (1.05x) (25)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.130%
Testing bit 29
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126329/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1036/1023 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 3 - 0.059%
Testing bit 30
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2107/2046 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 478 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64338/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.078%
Testing bit 31
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125635/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.072%
Testing bit 32
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125444/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4149/4090 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.094%
Testing bit 33
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 546/511 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 45 - 0.069%
Testing bit 34
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.133%
Testing bit 35
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 31 - 0.062%
Testing bit 36
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2121/2046 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.119%
Testing bit 37
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 77/63 (1.20x)
Testing collisions (low 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4123/4090 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.082%
Testing bit 38
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 542 (1.06x) (31)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1065/1023 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.058%
Testing bit 39
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 523 (1.02x) (12)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1056/1023 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 479 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125933/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 11 - 0.066%
Testing bit 40
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 47/31 (1.47x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 134/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.109%
Testing bit 41
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 474 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16336/16298 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1038/1023 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.067%
Testing bit 42
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 137/127 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.079%
Testing bit 43
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 38 - 0.089%
Testing bit 44
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing collisions (low 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.073%
Testing bit 45
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 47 - 0.092%
Testing bit 46
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 143/127 (1.12x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4172/4090 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.073%
Testing bit 47
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 263/255 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 560 (1.09x) (49)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 24 - 0.073%
Testing bit 48
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4230/4090 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.069%
Testing bit 49
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 265/255 (1.04x)
Testing collisions (low 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.077%
Testing bit 50
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.088%
Testing bit 51
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 461 (0.90x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2065/2046 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2045/2046 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 2 - 0.073%
Testing bit 52
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 527/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.083%
Testing bit 53
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 126008/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 18 - 0.083%
Testing bit 54
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 544/511 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.052%
Testing bit 55
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 454 (0.89x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125990/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8220/8170 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 21 - 0.108%
Testing bit 56
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 561 (1.10x) (50)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 148/127 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.067%
Testing bit 57
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 468 (0.91x)
Testing collisions (high 24-36 bits) - Worst is 26 bits: 32528/32429 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.111%
Testing bit 58
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 48/31 (1.50x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 264/255 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.080%
Testing bit 59
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2078/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2113/2046 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.088%
Testing bit 60
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8183/8170 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 38 - 0.067%
Testing bit 61
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.075%
Testing bit 62
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 572 (1.12x) (61)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 304/255 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.068%
Testing bit 63
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.089%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38919570.005124 - 273658.703091
Popcount 0 stats : 38918319.622388 - 273636.624134
MomentChi2 for bits 1 : 3.42946
MomentChi2 for bits 0 : 0.0261471
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919286.374871 - 273651.807467
Popcount 0 stats : 38918700.222160 - 273641.544934
MomentChi2 for deriv b1 : 2.15648
MomentChi2 for deriv b0 : 0.457215
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 130706 (1.00x) (-25)
Testing collisions (high 28-44 bits) - Worst is 44 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 130394 (1.00x) (-337)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 261/255 (1.02x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 681.305909 seconds
------------------------------------------------------------
```
------------------------------------------------------------
### Stable Hash 64bit SMHasher Results
The following results were achieved on my personal computer when testing the 64bit variant of the facil.io Stable Hash (`fio_stable_hash`).
```txt
------------------------------------------------------------
--- Testing Stable "facil.io Stable Hash 64bit" GOOD
[[[ Sanity Tests ]]]
Verification value 0x6993BB92 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 12.548 bytes/cycle - 35899.15 MiB/sec @ 3 ghz
Alignment 6 - 12.560 bytes/cycle - 35934.05 MiB/sec @ 3 ghz
Alignment 5 - 12.554 bytes/cycle - 35917.07 MiB/sec @ 3 ghz
Alignment 4 - 12.560 bytes/cycle - 35935.44 MiB/sec @ 3 ghz
Alignment 3 - 12.559 bytes/cycle - 35931.79 MiB/sec @ 3 ghz
Alignment 2 - 12.557 bytes/cycle - 35926.78 MiB/sec @ 3 ghz
Alignment 1 - 12.561 bytes/cycle - 35936.39 MiB/sec @ 3 ghz
Alignment 0 - 12.678 bytes/cycle - 36273.41 MiB/sec @ 3 ghz
Average - 12.572 bytes/cycle - 35969.26 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 20.57 cycles/hash
Small key speed test - 2-byte keys - 21.00 cycles/hash
Small key speed test - 3-byte keys - 21.00 cycles/hash
Small key speed test - 4-byte keys - 20.99 cycles/hash
Small key speed test - 5-byte keys - 21.00 cycles/hash
Small key speed test - 6-byte keys - 21.00 cycles/hash
Small key speed test - 7-byte keys - 22.00 cycles/hash
Small key speed test - 8-byte keys - 21.00 cycles/hash
Small key speed test - 9-byte keys - 21.00 cycles/hash
Small key speed test - 10-byte keys - 21.00 cycles/hash
Small key speed test - 11-byte keys - 21.00 cycles/hash
Small key speed test - 12-byte keys - 21.00 cycles/hash
Small key speed test - 13-byte keys - 21.00 cycles/hash
Small key speed test - 14-byte keys - 21.00 cycles/hash
Small key speed test - 15-byte keys - 21.00 cycles/hash
Small key speed test - 16-byte keys - 21.00 cycles/hash
Small key speed test - 17-byte keys - 22.88 cycles/hash
Small key speed test - 18-byte keys - 22.92 cycles/hash
Small key speed test - 19-byte keys - 22.94 cycles/hash
Small key speed test - 20-byte keys - 22.93 cycles/hash
Small key speed test - 21-byte keys - 22.94 cycles/hash
Small key speed test - 22-byte keys - 22.92 cycles/hash
Small key speed test - 23-byte keys - 22.89 cycles/hash
Small key speed test - 24-byte keys - 22.89 cycles/hash
Small key speed test - 25-byte keys - 22.93 cycles/hash
Small key speed test - 26-byte keys - 22.90 cycles/hash
Small key speed test - 27-byte keys - 23.03 cycles/hash
Small key speed test - 28-byte keys - 22.81 cycles/hash
Small key speed test - 29-byte keys - 22.89 cycles/hash
Small key speed test - 30-byte keys - 23.13 cycles/hash
Small key speed test - 31-byte keys - 22.99 cycles/hash
Average 21.953 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 155.249 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 93.391 cycles/op (1.6 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 143.332 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 93.590 cycles/op (1.3 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 87.173 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 49.115 cycles/op (0.5 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.632667%
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.704000%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.696667%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.686000%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.636000%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.630667%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.690667%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.670667%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.760667%
Testing 160-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.773333%
Testing 512-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.825333%
Testing 1024-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.753333%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 23 bits: 171/152 (1.12x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 21 bits: 602/606 (0.99x)
Testing distribution - Worst bias is the 13-bit window at bit 31 - 0.586%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 197 (1.05x) (9)
Testing collisions (high 24-35 bits) - Worst is 33 bits: 100/94 (1.06x)
Testing collisions (low 32-bit) - Expected 188.2, actual 170 (0.90x)
Testing collisions (low 24-35 bits) - Worst is 26 bits: 12071/11972 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 48 - 0.087%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2345 (0.99x) (-27)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 47/37 (1.27x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2374 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 30 bits: 9492/9478 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 35 - 0.057%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2380 (0.97x)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 163/153 (1.06x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2490 (1.01x) (30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 45/38 (1.17x)
Testing distribution - Worst bias is the 19-bit window at bit 50 - 0.061%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23277 (0.99x) (-160)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 28/22 (1.22x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23272 (0.99x) (-165)
Testing collisions (low 27-42 bits) - Worst is 30 bits: 93648/93442 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 29 - 0.022%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2065 (1.00x) (-3)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 37/32 (1.14x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2042 (0.99x) (-26)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 40/32 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 37 - 0.050%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8020 (1.00x) (-1)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 36/31 (1.15x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8038 (1.00x) (17)
Testing collisions (low 26-40 bits) - Worst is 31 bits: 16101/16033 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.043%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26679 (1.01x) (228)
Testing collisions (high 27-42 bits) - Worst is 39 bits: 218/206 (1.05x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26434 (1.00x) (-17)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3324/3309 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 26 - 0.028%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1462 (1.04x) (62)
Testing collisions (high 25-38 bits) - Worst is 37 bits: 53/43 (1.21x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1406 (1.00x) (6)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 60/43 (1.37x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.088%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84527 (1.00x) (-19)
Testing collisions (high 28-44 bits) - Worst is 44 bits: 29/20 (1.40x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84586 (1.00x) (40)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 172/165 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 39 - 0.017%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 947 (1.04x) (37)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 63/56 (1.11x)
Testing collisions (low 32-bit) - Expected 910.2, actual 925 (1.02x) (15)
Testing collisions (low 25-37 bits) - Worst is 33 bits: 473/455 (1.04x)
Testing distribution - Worst bias is the 19-bit window at bit 50 - 0.082%
Keyset 'Sparse' - 512-bit keys with up to 3 bits set - 22370049 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 58155.4, actual 57664 (0.99x) (-491)
Testing collisions (high 28-43 bits) - Worst is 38 bits: 924/910 (1.02x)
Testing collisions (low 32-bit) - Expected 58155.4, actual 58193 (1.00x) (38)
Testing collisions (low 28-43 bits) - Worst is 39 bits: 498/455 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 23 - 0.017%
Keyset 'Sparse' - 1024-bit keys with up to 2 bits set - 524801 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32.1, actual 30 (0.94x)
Testing collisions (high 22-32 bits) - Worst is 28 bits: 518/512 (1.01x)
Testing collisions (low 32-bit) - Expected 32.1, actual 23 (0.72x)
Testing collisions (low 22-32 bits) - Worst is 29 bits: 294/256 (1.15x)
Testing distribution - Worst bias is the 16-bit window at bit 48 - 0.155%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 512.4, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4139/4094 (1.01x)
Testing collisions (low 32-bit) - Expected 512.4, actual 529 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 143/128 (1.12x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.057%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 670 (1.00x) (2)
Testing collisions (high 24-37 bits) - Worst is 36 bits: 44/41 (1.05x)
Testing collisions (low 32-bit) - Expected 668.6, actual 655 (0.98x)
Testing collisions (low 24-37 bits) - Worst is 34 bits: 173/167 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.080%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 657 (0.98x) (-11)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 24/20 (1.15x)
Testing collisions (low 32-bit) - Expected 668.6, actual 691 (1.03x) (23)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 362/334 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.047%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17106 (0.99x) (-216)
Testing collisions (high 27-41 bits) - Worst is 41 bits: 37/33 (1.09x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17214 (0.99x) (-108)
Testing collisions (low 27-41 bits) - Worst is 28 bits: 273805/273271 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.030%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8288 (1.01x) (102)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8135 (0.99x) (-51)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 52 - 0.038%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8207 (1.00x) (21)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8100 (0.99x) (-86)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.036%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 7983 (0.98x)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8152 (1.00x) (-34)
Testing collisions (low 26-40 bits) - Worst is 36 bits: 525/511 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 4 - 0.054%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8201 (1.00x) (15)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8064 (0.99x) (-122)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.040%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8156 (1.00x) (-30)
Testing collisions (high 26-40 bits) - Worst is 28 bits: 130239/129717 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8355 (1.02x) (169)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 44 - 0.040%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8046 (0.98x) (-140)
Testing collisions (high 26-40 bits) - Worst is 36 bits: 550/511 (1.07x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8034 (0.98x) (-152)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 265/255 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 24 - 0.042%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8155 (1.00x) (-31)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 84/63 (1.31x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8295 (1.01x) (109)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 20-bit window at bit 4 - 0.036%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8248 (1.01x) (62)
Testing collisions (high 26-40 bits) - Worst is 37 bits: 279/255 (1.09x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8246 (1.01x) (60)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 20-bit window at bit 51 - 0.040%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8174 (1.00x) (-12)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 136/127 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8053 (0.98x) (-133)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.043%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8175 (1.00x) (-11)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 68/63 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8116 (0.99x) (-70)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 20-bit window at bit 10 - 0.042%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8223 (1.00x) (37)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 130/127 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8294 (1.01x) (108)
Testing collisions (low 26-40 bits) - Worst is 38 bits: 149/127 (1.16x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.045%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8163 (1.00x) (-23)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8198 (1.00x) (12)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1055/1023 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 45 - 0.041%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 104 (0.89x)
Testing collisions (high 23-34 bits) - Worst is 24 bits: 29355/29218 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 107 (0.92x)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing distribution - Worst bias is the 17-bit window at bit 20 - 0.156%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 117 (1.01x) (1)
Testing collisions (high 23-34 bits) - Worst is 32 bits: 117/116 (1.01x)
Testing collisions (low 32-bit) - Expected 116.4, actual 111 (0.95x)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 40 - 0.129%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 95 (0.82x)
Testing collisions (high 23-34 bits) - Worst is 24 bits: 29375/29218 (1.01x)
Testing collisions (low 32-bit) - Expected 116.4, actual 136 (1.17x) (20)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 12 - 0.166%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 110 (0.94x)
Testing collisions (high 23-34 bits) - Worst is 23 bits: 57228/57305 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 128 (1.10x) (12)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing distribution - Worst bias is the 17-bit window at bit 40 - 0.096%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 30/29 (1.03x)
Testing collisions (low 32-bit) - Expected 116.4, actual 111 (0.95x)
Testing collisions (low 23-34 bits) - Worst is 24 bits: 29234/29218 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 27 - 0.099%
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 128 (1.10x) (12)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 930/930 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 30 - 0.148%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (high 23-33 bits) - Worst is 33 bits: 29/24 (1.17x)
Testing collisions (low 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (low 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 16 - 0.189%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3504 (1.01x) (21)
Testing collisions (high 26-39 bits) - Worst is 39 bits: 31/27 (1.14x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3493 (1.00x) (10)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 38/27 (1.40x)
Testing distribution - Worst bias is the 20-bit window at bit 52 - 0.096%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 40041 (0.99x) (-248)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 45/39 (1.14x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40866 (1.01x) (577)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 177/157 (1.12x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.021%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 227182.3, actual 227276 (1.00x) (94)
Testing collisions (high 29-45 bits) - Worst is 40 bits: 938/890 (1.05x)
Testing collisions (low 32-bit) - Expected 227182.3, actual 227236 (1.00x) (54)
Testing collisions (low 29-45 bits) - Worst is 45 bits: 37/27 (1.33x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.006%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 865959.1, actual 866307 (1.00x) (348)
Testing collisions (high 30-47 bits) - Worst is 37 bits: 27622/27237 (1.01x)
Testing collisions (low 32-bit) - Expected 865959.1, actual 865094 (1.00x) (-865)
Testing collisions (low 30-47 bits) - Worst is 46 bits: 68/53 (1.28x)
Testing distribution - Worst bias is the 20-bit window at bit 37 - 0.004%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25376 (1.00x) (-13)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 26/24 (1.05x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25263 (1.00x) (-126)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 425/397 (1.07x)
Testing distribution - Worst bias is the 19-bit window at bit 27 - 0.018%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25364 (1.00x) (-25)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 27/24 (1.09x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25570 (1.01x) (181)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 30/24 (1.21x)
Testing distribution - Worst bias is the 20-bit window at bit 13 - 0.031%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25556 (1.01x) (167)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 63/49 (1.27x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25622 (1.01x) (233)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 212/198 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 54 - 0.031%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1859 (1.00x) (-3)
Testing collisions (high 25-38 bits) - Worst is 37 bits: 70/58 (1.20x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1926 (1.03x) (64)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 30 - 0.067%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1860 (1.00x) (-2)
Testing collisions (high 25-38 bits) - Worst is 27 bits: 59197/59016 (1.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1829 (0.98x) (-33)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 228540/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 7 - 0.042%
Keyset 'Words' - 466569 dict words
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 31 (1.22x) (6)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 31/25 (1.22x)
Testing collisions (low 32-bit) - Expected 25.3, actual 20 (0.79x)
Testing collisions (low 22-32 bits) - Worst is 26 bits: 1650/1618 (1.02x)
Testing distribution - Worst bias is the 16-bit window at bit 1 - 0.220%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 5 (1.02x) (1)
Testing collisions (high 21-29 bits) - Worst is 29 bits: 44/39 (1.13x)
Testing collisions (low 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (low 21-29 bits) - Worst is 29 bits: 49/39 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 29 - 0.313%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2994 (1.03x) (85)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 103/90 (1.13x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 2973 (1.02x) (64)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 36/22 (1.58x)
Testing distribution - Worst bias is the 19-bit window at bit 10 - 0.042%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32957 (1.01x) (232)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32857 (1.00x) (132)
Testing collisions (low 27-42 bits) - Worst is 41 bits: 69/63 (1.08x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1110 (0.99x) (-6)
Testing collisions (high 25-37 bits) - Worst is 31 bits: 2271/2231 (1.02x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1068 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 37 bits: 42/34 (1.20x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 473 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 145/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.095%
Testing bit 1
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing collisions (low 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 17-bit window at bit 23 - 0.077%
Testing bit 2
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 479 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8185/8170 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.062%
Testing bit 3
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 490 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2047/2046 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8326/8170 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.061%
Testing bit 4
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 463 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64159/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.076%
Testing bit 5
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 58 - 0.071%
Testing bit 6
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 17-bit window at bit 28 - 0.054%
Testing bit 7
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 278/255 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 17-bit window at bit 60 - 0.058%
Testing bit 8
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.088%
Testing bit 9
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.063%
Testing bit 10
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 131/127 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.081%
Testing bit 11
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 67/63 (1.05x)
Testing distribution - Worst bias is the 17-bit window at bit 45 - 0.064%
Testing bit 12
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.086%
Testing bit 13
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 517/511 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 471 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125649/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.071%
Testing bit 14
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.104%
Testing bit 15
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 513/511 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2182/2046 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.062%
Testing bit 16
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.103%
Testing bit 17
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64414/64191 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.090%
Testing bit 18
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.072%
Testing bit 19
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 45 - 0.055%
Testing bit 20
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 283/255 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 548 (1.07x) (37)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.061%
Testing bit 21
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 489 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4170/4090 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.063%
Testing bit 22
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.111%
Testing bit 23
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 486 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8215/8170 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.096%
Testing bit 24
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.085%
Testing bit 25
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 2 - 0.072%
Testing bit 26
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1046/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 44 - 0.060%
Testing bit 27
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.067%
Testing bit 28
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1027/1023 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 44 - 0.072%
Testing bit 29
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 560 (1.09x) (49)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 153/127 (1.20x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 17-bit window at bit 59 - 0.067%
Testing bit 30
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4102/4090 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 516/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.084%
Testing bit 31
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 549/511 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.070%
Testing bit 32
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.086%
Testing bit 33
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 536 (1.05x) (25)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 568 (1.11x) (57)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.075%
Testing bit 34
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 257/255 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 24 - 0.047%
Testing bit 35
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 28 - 0.061%
Testing bit 36
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2062/2046 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.092%
Testing bit 37
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.085%
Testing bit 38
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 489 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 556/511 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.045%
Testing bit 39
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 282/255 (1.10x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.081%
Testing bit 40
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 459 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.095%
Testing bit 41
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.100%
Testing bit 42
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1041/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.080%
Testing bit 43
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 44
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2095/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.088%
Testing bit 45
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 559 (1.09x) (48)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.073%
Testing bit 46
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.073%
Testing bit 47
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 472 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125700/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 11 - 0.084%
Testing bit 48
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 26 bits: 32601/32429 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.053%
Testing bit 49
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1046/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.074%
Testing bit 50
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 56 - 0.061%
Testing bit 51
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.079%
Testing bit 52
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8213/8170 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 28 - 0.110%
Testing bit 53
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.073%
Testing bit 54
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 283/255 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.079%
Testing bit 55
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.066%
Testing bit 56
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4143/4090 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 18-bit window at bit 17 - 0.112%
Testing bit 57
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.142%
Testing bit 58
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 131/127 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 59
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 541 (1.06x) (30)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.056%
Testing bit 60
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 82/63 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.083%
Testing bit 61
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 545 (1.06x) (34)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 545/511 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.083%
Testing bit 62
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.095%
Testing bit 63
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 264/255 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.077%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38918998.920034 - 273638.558723
Popcount 0 stats : 38918770.531038 - 273645.996303
MomentChi2 for bits 1 : 1.16628
MomentChi2 for bits 0 : 0.594771
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919257.619283 - 273628.012809
Popcount 0 stats : 38918472.697479 - 273650.071385
MomentChi2 for deriv b1 : 2.04392
MomentChi2 for deriv b0 : 0.135878
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 130884 (1.00x) (153)
Testing collisions (high 28-44 bits) - Worst is 43 bits: 75/63 (1.17x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 131211 (1.00x) (480)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 68/63 (1.06x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 755.424002 seconds
------------------------------------------------------------
```
------------------------------------------------------------
### Stable Hash 128bit SMHasher Results
The following results were achieved on my personal computer when testing the 128bit variant of the facil.io Stable Hash (`fio_stable_hash128`).
```txt
------------------------------------------------------------
--- Testing Stable128 "facil.io Stable Hash 128bit" GOOD
[[[ Sanity Tests ]]]
Verification value 0x97D1AB52 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 12.633 bytes/cycle - 36143.93 MiB/sec @ 3 ghz
Alignment 6 - 12.651 bytes/cycle - 36193.49 MiB/sec @ 3 ghz
Alignment 5 - 12.646 bytes/cycle - 36179.39 MiB/sec @ 3 ghz
Alignment 4 - 12.644 bytes/cycle - 36174.76 MiB/sec @ 3 ghz
Alignment 3 - 12.656 bytes/cycle - 36208.15 MiB/sec @ 3 ghz
Alignment 2 - 12.650 bytes/cycle - 36192.29 MiB/sec @ 3 ghz
Alignment 1 - 12.644 bytes/cycle - 36175.29 MiB/sec @ 3 ghz
Alignment 0 - 12.797 bytes/cycle - 36612.89 MiB/sec @ 3 ghz
Average - 12.665 bytes/cycle - 36235.02 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 21.00 cycles/hash
Small key speed test - 2-byte keys - 21.81 cycles/hash
Small key speed test - 3-byte keys - 22.97 cycles/hash
Small key speed test - 4-byte keys - 21.99 cycles/hash
Small key speed test - 5-byte keys - 22.69 cycles/hash
Small key speed test - 6-byte keys - 22.00 cycles/hash
Small key speed test - 7-byte keys - 23.92 cycles/hash
Small key speed test - 8-byte keys - 21.98 cycles/hash
Small key speed test - 9-byte keys - 21.99 cycles/hash
Small key speed test - 10-byte keys - 21.99 cycles/hash
Small key speed test - 11-byte keys - 21.99 cycles/hash
Small key speed test - 12-byte keys - 21.99 cycles/hash
Small key speed test - 13-byte keys - 21.99 cycles/hash
Small key speed test - 14-byte keys - 21.99 cycles/hash
Small key speed test - 15-byte keys - 21.99 cycles/hash
Small key speed test - 16-byte keys - 22.00 cycles/hash
Small key speed test - 17-byte keys - 23.98 cycles/hash
Small key speed test - 18-byte keys - 23.98 cycles/hash
Small key speed test - 19-byte keys - 23.98 cycles/hash
Small key speed test - 20-byte keys - 23.98 cycles/hash
Small key speed test - 21-byte keys - 23.97 cycles/hash
Small key speed test - 22-byte keys - 23.98 cycles/hash
Small key speed test - 23-byte keys - 23.98 cycles/hash
Small key speed test - 24-byte keys - 23.97 cycles/hash
Small key speed test - 25-byte keys - 23.97 cycles/hash
Small key speed test - 26-byte keys - 23.97 cycles/hash
Small key speed test - 27-byte keys - 23.97 cycles/hash
Small key speed test - 28-byte keys - 23.97 cycles/hash
Small key speed test - 29-byte keys - 23.97 cycles/hash
Small key speed test - 30-byte keys - 23.98 cycles/hash
Small key speed test - 31-byte keys - 24.18 cycles/hash
Average 23.037 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 151.297 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 93.079 cycles/op (1.2 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 145.520 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 91.867 cycles/op (0.7 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 84.812 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 48.436 cycles/op (0.4 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.659333%
Testing 32-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 40-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.723333%
Testing 48-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.696667%
Testing 56-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.694000%
Testing 64-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 72-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.676000%
Testing 80-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.704667%
Testing 96-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.690667%
Testing 112-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.721333%
Testing 128-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.760667%
Testing 160-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.773333%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 23 bits: 161/152 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 21 bits: 602/606 (0.99x)
Testing distribution - Worst bias is the 13-bit window at bit 31 - 0.586%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 190 (1.01x) (2)
Testing collisions (high 24-35 bits) - Worst is 32 bits: 190/188 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 188.2, actual 170 (0.90x)
Testing collisions (low 24-35 bits) - Worst is 26 bits: 12071/11972 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 35 - 0.080%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2355 (0.99x) (-17)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 39/37 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2374 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 30 bits: 9492/9478 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 74 - 0.065%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2429 (0.99x) (-31)
Testing collisions (high 25-38 bits) - Worst is 29 bits: 19779/19637 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2490 (1.01x) (30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 45/38 (1.17x)
Testing distribution - Worst bias is the 19-bit window at bit 82 - 0.051%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23636 (1.01x) (199)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 24/22 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23272 (0.99x) (-165)
Testing collisions (low 27-42 bits) - Worst is 30 bits: 93648/93442 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 74 - 0.028%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2045 (0.99x) (-23)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 33/32 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2042 (0.99x) (-26)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 40/32 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 96 - 0.061%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8104 (1.01x) (83)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 39/31 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8038 (1.00x) (17)
Testing collisions (low 26-40 bits) - Worst is 31 bits: 16101/16033 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.043%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26433 (1.00x) (-18)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 55/51 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26434 (1.00x) (-17)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3324/3309 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 26 - 0.028%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1326 (0.95x)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 22/21 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1406 (1.00x) (6)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 60/43 (1.37x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.088%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84181 (1.00x) (-365)
Testing collisions (high 28-44 bits) - Worst is 43 bits: 54/41 (1.31x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84586 (1.00x) (40)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 172/165 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 39 - 0.017%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 920 (1.01x) (10)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 65/56 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 910.2, actual 925 (1.02x) (15)
Testing collisions (low 25-37 bits) - Worst is 33 bits: 473/455 (1.04x)
Testing distribution - Worst bias is the 19-bit window at bit 73 - 0.096%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 735 (1.10x) (67)
Testing collisions (high 24-37 bits) - Worst is 34 bits: 207/167 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 668.6, actual 655 (0.98x)
Testing collisions (low 24-37 bits) - Worst is 34 bits: 173/167 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.080%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 681 (1.02x) (13)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 26/20 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 668.6, actual 691 (1.03x) (23)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 362/334 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 68 - 0.091%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17433 (1.01x) (111)
Testing collisions (high 27-41 bits) - Worst is 34 bits: 4416/4333 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17214 (0.99x) (-108)
Testing collisions (low 27-41 bits) - Worst is 28 bits: 273805/273271 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.030%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 135/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 12 (1.50x) (5)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 15-bit window at bit 48 - 0.268%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 26 bits: 537/511 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 15-bit window at bit 119 - 0.328%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 1 (0.13x)
Testing collisions (high 21-30 bits) - Worst is 24 bits: 2103/2037 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 15-bit window at bit 15 - 0.279%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 29 bits: 72/63 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 10 (1.25x) (3)
Testing collisions (low 21-30 bits) - Worst is 24 bits: 2041/2037 (1.00x)
Testing distribution - Worst bias is the 15-bit window at bit 2 - 0.203%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 3 (0.38x)
Testing collisions (high 21-30 bits) - Worst is 30 bits: 33/31 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 15 (1.88x) (8)
Testing collisions (low 21-30 bits) - Worst is 27 bits: 262/255 (1.02x)
Testing distribution - Worst bias is the 15-bit window at bit 21 - 0.203%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 132/127 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (low 21-30 bits) - Worst is 25 bits: 1037/1021 (1.02x)
Testing distribution - Worst bias is the 15-bit window at bit 47 - 0.252%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 13 (1.63x) (6)
Testing collisions (high 21-30 bits) - Worst is 30 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 6 (0.75x)
Testing collisions (low 21-30 bits) - Worst is 25 bits: 1034/1021 (1.01x)
Testing distribution - Worst bias is the 15-bit window at bit 43 - 0.259%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 5 (0.63x)
Testing collisions (high 21-30 bits) - Worst is 29 bits: 67/63 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 4 (0.50x)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 15-bit window at bit 24 - 0.293%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (high 21-30 bits) - Worst is 22 bits: 8144/8023 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 3 (0.38x)
Testing collisions (low 21-30 bits) - Worst is 28 bits: 151/127 (1.18x)
Testing distribution - Worst bias is the 15-bit window at bit 74 - 0.329%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (high 21-30 bits) - Worst is 27 bits: 286/255 (1.12x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 4 (0.50x)
Testing collisions (low 21-30 bits) - Worst is 24 bits: 2098/2037 (1.03x)
Testing distribution - Worst bias is the 15-bit window at bit 118 - 0.211%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (high 21-30 bits) - Worst is 26 bits: 553/511 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 15-bit window at bit 57 - 0.323%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 11 (1.38x) (4)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 136/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 13 (1.63x) (6)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 125 - 0.223%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 33 bits: 63/58 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 930/930 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 30 - 0.148%
Keyset 'Cyclic' - 8 cycles of 17 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1937/1860 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 123 (1.06x) (7)
Testing collisions (low 23-34 bits) - Worst is 31 bits: 264/232 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 38 - 0.131%
Keyset 'Cyclic' - 8 cycles of 18 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 104 (0.89x)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1911/1860 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 113 (0.97x)
Testing collisions (low 23-34 bits) - Worst is 31 bits: 240/232 (1.03x)
Testing distribution - Worst bias is the 17-bit window at bit 58 - 0.123%
Keyset 'Cyclic' - 8 cycles of 19 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 33/29 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 100 (0.86x)
Testing collisions (low 23-34 bits) - Worst is 28 bits: 1891/1860 (1.02x)
Testing distribution - Worst bias is the 17-bit window at bit 4 - 0.103%
Keyset 'Cyclic' - 8 cycles of 20 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 97 (0.83x)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1903/1860 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 125 (1.07x) (9)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 32 - 0.172%
Keyset 'Cyclic' - 8 cycles of 24 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 32/29 (1.10x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 131 (1.13x) (15)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 38/29 (1.31x)
Testing distribution - Worst bias is the 17-bit window at bit 11 - 0.148%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 50 (1.01x) (1)
Testing collisions (high 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (low 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 16 - 0.189%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3479 (1.00x) (-4)
Testing collisions (high 26-39 bits) - Worst is 35 bits: 484/435 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3493 (1.00x) (10)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 38/27 (1.40x)
Testing distribution - Worst bias is the 20-bit window at bit 11 - 0.064%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 39913 (0.99x) (-376)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 81/78 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40866 (1.01x) (577)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 177/157 (1.12x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.021%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25586 (1.01x) (197)
Testing collisions (high 27-42 bits) - Worst is 38 bits: 422/397 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25263 (1.00x) (-126)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 425/397 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 57 - 0.021%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25629 (1.01x) (240)
Testing collisions (high 27-42 bits) - Worst is 34 bits: 6431/6352 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25570 (1.01x) (181)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 30/24 (1.21x)
Testing distribution - Worst bias is the 20-bit window at bit 13 - 0.031%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25481 (1.00x) (92)
Testing collisions (high 27-42 bits) - Worst is 40 bits: 111/99 (1.12x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25622 (1.01x) (233)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 212/198 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 110 - 0.026%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1878 (1.01x) (16)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 490/465 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1926 (1.03x) (64)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 30 - 0.067%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1898 (1.02x) (36)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 138/116 (1.19x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1829 (0.98x) (-33)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 228540/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 110 - 0.058%
Keyset 'Words' - 466569 dict words
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 34 (1.34x) (9)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 34/25 (1.34x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25.3, actual 20 (0.79x)
Testing collisions (low 22-32 bits) - Worst is 26 bits: 1650/1618 (1.02x)
Testing distribution - Worst bias is the 16-bit window at bit 1 - 0.220%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 7 (1.43x) (3)
Testing collisions (high 21-29 bits) - Worst is 29 bits: 48/39 (1.23x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (low 21-29 bits) - Worst is 29 bits: 49/39 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 29 - 0.313%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2778 (0.95x)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 96/90 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 2973 (1.02x) (64)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 36/22 (1.58x)
Testing distribution - Worst bias is the 19-bit window at bit 79 - 0.055%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32905 (1.01x) (180)
Testing collisions (high 27-42 bits) - Worst is 40 bits: 142/127 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32857 (1.00x) (132)
Testing collisions (low 27-42 bits) - Worst is 41 bits: 69/63 (1.08x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1102 (0.99x) (-14)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 83/69 (1.19x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1068 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 37 bits: 42/34 (1.20x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 128 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 128 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 128 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 145/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.123%
Testing bit 1
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2065/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.092%
Testing bit 2
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 442 (0.86x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4169/4090 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 107 - 0.080%
Testing bit 3
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8326/8170 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 113 - 0.077%
Testing bit 4
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4107/4090 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 463 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64159/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.076%
Testing bit 5
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 482 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8318/8170 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 82 - 0.070%
Testing bit 6
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 127 - 0.104%
Testing bit 7
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16399/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 106 - 0.081%
Testing bit 8
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.088%
Testing bit 9
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 148/127 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing distribution - Worst bias is the 18-bit window at bit 123 - 0.110%
Testing bit 10
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 89 - 0.083%
Testing bit 11
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 67/63 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 68 - 0.087%
Testing bit 12
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.095%
Testing bit 13
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 471 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125649/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 119 - 0.082%
Testing bit 14
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 83/63 (1.30x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.073%
Testing bit 15
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2182/2046 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.104%
Testing bit 16
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 277/255 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 108 - 0.101%
Testing bit 17
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.090%
Testing bit 18
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 81 - 0.101%
Testing bit 19
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 563 (1.10x) (52)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.106%
Testing bit 20
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1041/1023 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 548 (1.07x) (37)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 61 - 0.071%
Testing bit 21
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 121 - 0.092%
Testing bit 22
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 63947/64191 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.111%
Testing bit 23
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 555 (1.08x) (44)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 555/511 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.096%
Testing bit 24
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 486 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64468/64191 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.109%
Testing bit 25
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.081%
Testing bit 26
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 485 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16451/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 75 - 0.093%
Testing bit 27
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16466/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 107 - 0.098%
Testing bit 28
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1060/1023 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1027/1023 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 80 - 0.107%
Testing bit 29
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126291/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.088%
Testing bit 30
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 516/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.084%
Testing bit 31
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.070%
Testing bit 32
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2072/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.086%
Testing bit 33
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 568 (1.11x) (57)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.097%
Testing bit 34
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 257/255 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.087%
Testing bit 35
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 96 - 0.066%
Testing bit 36
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 77/63 (1.20x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2062/2046 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.121%
Testing bit 37
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 121 - 0.093%
Testing bit 38
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1045/1023 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 556/511 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 123 - 0.077%
Testing bit 39
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 470 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 83/63 (1.30x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 282/255 (1.10x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.081%
Testing bit 40
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16404/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 459 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.095%
Testing bit 41
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.100%
Testing bit 42
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.076%
Testing bit 43
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 475 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125780/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 44
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2095/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.068%
Testing bit 45
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 117 - 0.078%
Testing bit 46
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1087/1023 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.104%
Testing bit 47
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2085/2046 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 82 - 0.085%
Testing bit 48
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 523 (1.02x) (12)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 73 - 0.081%
Testing bit 49
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2143/2046 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 66 - 0.099%
Testing bit 50
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 119 - 0.103%
Testing bit 51
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 78 - 0.090%
Testing bit 52
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2076/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 28 - 0.110%
Testing bit 53
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.073%
Testing bit 54
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 71 - 0.098%
Testing bit 55
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing distribution - Worst bias is the 18-bit window at bit 87 - 0.087%
Testing bit 56
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 45/31 (1.41x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 18-bit window at bit 17 - 0.112%
Testing bit 57
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 475 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126113/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.089%
Testing bit 58
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 557 (1.09x) (46)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 59
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1082/1023 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 541 (1.06x) (30)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 55 - 0.065%
Testing bit 60
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.083%
Testing bit 61
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 48/31 (1.50x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.083%
Testing bit 62
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126044/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.095%
Testing bit 63
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 543 (1.06x) (32)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 543/511 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.077%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38918998.920034 - 273638.558723
Popcount 0 stats : 38918770.531038 - 273645.996303
MomentChi2 for bits 1 : 1.16628
MomentChi2 for bits 0 : 0.594771
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919257.619283 - 273628.012809
Popcount 0 stats : 38918472.697479 - 273650.071385
MomentChi2 for deriv b1 : 2.04392
MomentChi2 for deriv b0 : 0.135878
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 131029 (1.00x) (298)
Testing collisions (high 28-44 bits) - Worst is 37 bits: 4158/4095 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 130672 (1.00x) (-59)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 85/63 (1.33x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 1063.513065 seconds
------------------------------------------------------------
```
------------------------------------------------------------
## Pseudo-Random Function Testing
Testing the Pseudo-Random Number Generator (PRNG) Functions isn't somewhat of a chore, as a complete test could take a week or so to complete and my laptop wasn't designed for such long running intensive tasks.
Tests were conducted by utilizing [PractRand](https://pracrand.sourceforge.net) as well as some tests adopted from the [xoshiro](http://xoshiro.di.unimi.it/hwd.php) testing code, running both up to 2TB of random data (which took about 1 night for PractRand).
Results are as follows:
------------------------------------------------------------
### `fio_rand`
The following are the tests for the core `fio_rand64` and `fio_rand_bytes` functions provided when using `FIO_RAND`.
**The `PractRand` results**:
```txt
# ./tmp/rnd -p fio | RNG_test stdin
RNG_test using PractRand version 0.95
RNG = RNG_stdin, seed = unknown
test set = core, folding = standard(unknown format)
rng=RNG_stdin, seed=unknown
length= 256 megabytes (2^28 bytes), time= 3.0 seconds
no anomalies in 217 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 megabytes (2^29 bytes), time= 6.5 seconds
no anomalies in 232 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 gigabyte (2^30 bytes), time= 13.0 seconds
no anomalies in 251 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 25.2 seconds
no anomalies in 269 test result(s)
rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 48.2 seconds
no anomalies in 283 test result(s)
rng=RNG_stdin, seed=unknown
length= 8 gigabytes (2^33 bytes), time= 96.5 seconds
Test Name Raw Processed Evaluation
BCFN(2+7,13-2,T) R= -9.1 p =1-3.9e-5 unusual
...and 299 test result(s) without anomalies
rng=RNG_stdin, seed=unknown
length= 16 gigabytes (2^34 bytes), time= 190 seconds
no anomalies in 315 test result(s)
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 376 seconds
no anomalies in 328 test result(s)
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 752 seconds
no anomalies in 344 test result(s)
rng=RNG_stdin, seed=unknown
length= 128 gigabytes (2^37 bytes), time= 1498 seconds
no anomalies in 359 test result(s)
rng=RNG_stdin, seed=unknown
length= 256 gigabytes (2^38 bytes), time= 2978 seconds
no anomalies in 372 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 gigabytes (2^39 bytes), time= 9897 seconds
no anomalies in 387 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 terabyte (2^40 bytes), time= 21004 seconds
no anomalies in 401 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 terabytes (2^41 bytes), time= 43108 seconds
no anomalies in 413 test result(s)
```
**The tests adopted from the `xoshiro` code base**:
```txt
# ./tmp/random fio
mix3 extreme = 1.80533 (sig = 00100000) weight 1 (16), p-value = 0.692
mix3 extreme = 2.35650 (sig = 00000102) weight 2 (112), p-value = 0.876
mix3 extreme = 3.33991 (sig = 20002100) weight 3 (448), p-value = 0.313
mix3 extreme = 4.30088 (sig = 02210200) weight 4 (1120), p-value = 0.0189
mix3 extreme = 4.03228 (sig = 01002212) weight >=5 (4864), p-value = 0.236
bits per word = 64 (analyzing bits); min category p-value = 0.0189
processed 1.11e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.0909
------
mix3 extreme = 1.84047 (sig = 20000000) weight 1 (16), p-value = 0.663
mix3 extreme = 3.12258 (sig = 00000102) weight 2 (112), p-value = 0.182
mix3 extreme = 3.47031 (sig = 20002100) weight 3 (448), p-value = 0.208
mix3 extreme = 3.61471 (sig = 12000101) weight 4 (1120), p-value = 0.286
mix3 extreme = 3.76213 (sig = 10111202) weight >=5 (4864), p-value = 0.559
bits per word = 64 (analyzing bits); min category p-value = 0.182
processed 1.29e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.634
------
mix3 extreme = 1.65805 (sig = 00000002) weight 1 (16), p-value = 0.806
mix3 extreme = 3.09464 (sig = 00000102) weight 2 (112), p-value = 0.198
mix3 extreme = 3.53210 (sig = 00102010) weight 3 (448), p-value = 0.169
mix3 extreme = 3.35900 (sig = 02210200) weight 4 (1120), p-value = 0.584
mix3 extreme = 3.58826 (sig = 02011101) weight >=5 (4864), p-value = 0.802
bits per word = 64 (analyzing bits); min category p-value = 0.169
processed 1.66e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.603
------
mix3 extreme = 1.75332 (sig = 00000001) weight 1 (16), p-value = 0.735
mix3 extreme = 2.97345 (sig = 00000102) weight 2 (112), p-value = 0.281
mix3 extreme = 3.21121 (sig = 20002100) weight 3 (448), p-value = 0.447
mix3 extreme = 2.99207 (sig = 10102020) weight 4 (1120), p-value = 0.955
mix3 extreme = 3.53616 (sig = 21120101) weight >=5 (4864), p-value = 0.861
bits per word = 64 (analyzing bits); min category p-value = 0.281
processed 1.85e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.808
------
mix3 extreme = 1.83225 (sig = 00000100) weight 1 (16), p-value = 0.67
mix3 extreme = 3.10258 (sig = 00000102) weight 2 (112), p-value = 0.194
mix3 extreme = 3.03291 (sig = 20002100) weight 3 (448), p-value = 0.663
mix3 extreme = 3.09549 (sig = 10200022) weight 4 (1120), p-value = 0.89
mix3 extreme = 3.76428 (sig = 02011101) weight >=5 (4864), p-value = 0.556
bits per word = 64 (analyzing bits); min category p-value = 0.194
processed 2.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.659
------
mix3 extreme = 1.84289 (sig = 00000100) weight 1 (16), p-value = 0.661
mix3 extreme = 3.57529 (sig = 00000102) weight 2 (112), p-value = 0.0384
mix3 extreme = 3.46502 (sig = 20002010) weight 3 (448), p-value = 0.211
mix3 extreme = 3.25764 (sig = 00220201) weight 4 (1120), p-value = 0.716
mix3 extreme = 4.19914 (sig = 11222212) weight >=5 (4864), p-value = 0.122
bits per word = 64 (analyzing bits); min category p-value = 0.0384
processed 2.59e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.178
------
mix3 extreme = 2.19203 (sig = 00000002) weight 1 (16), p-value = 0.369
mix3 extreme = 3.04970 (sig = 00000102) weight 2 (112), p-value = 0.227
mix3 extreme = 3.44778 (sig = 20002010) weight 3 (448), p-value = 0.224
mix3 extreme = 3.66866 (sig = 22110000) weight 4 (1120), p-value = 0.239
mix3 extreme = 3.58667 (sig = 02021021) weight >=5 (4864), p-value = 0.804
bits per word = 64 (analyzing bits); min category p-value = 0.224
processed 3.14e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.718
------
mix3 extreme = 2.51064 (sig = 00000002) weight 1 (16), p-value = 0.176
mix3 extreme = 2.57871 (sig = 20010000) weight 2 (112), p-value = 0.672
mix3 extreme = 3.85273 (sig = 20002010) weight 3 (448), p-value = 0.051
mix3 extreme = 3.18862 (sig = 01101010) weight 4 (1120), p-value = 0.799
mix3 extreme = 3.60146 (sig = 22121010) weight >=5 (4864), p-value = 0.785
bits per word = 64 (analyzing bits); min category p-value = 0.051
processed 4.07e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.23
------
mix3 extreme = 1.89834 (sig = 00000002) weight 1 (16), p-value = 0.613
mix3 extreme = 2.27677 (sig = 00100200) weight 2 (112), p-value = 0.924
mix3 extreme = 3.52766 (sig = 20000022) weight 3 (448), p-value = 0.171
mix3 extreme = 3.50133 (sig = 11000102) weight 4 (1120), p-value = 0.405
mix3 extreme = 3.47778 (sig = 11121011) weight >=5 (4864), p-value = 0.915
bits per word = 64 (analyzing bits); min category p-value = 0.171
processed 5.18e+08 bytes in 1 seconds (0.5177 GB/s, 1.864 TB/h). Fri Feb 21 23:22:22 2025
p = 0.609
------
mix3 extreme = 1.99814 (sig = 00000001) weight 1 (16), p-value = 0.527
mix3 extreme = 2.43362 (sig = 00100200) weight 2 (112), p-value = 0.815
mix3 extreme = 3.46309 (sig = 20002010) weight 3 (448), p-value = 0.213
mix3 extreme = 3.67295 (sig = 20110001) weight 4 (1120), p-value = 0.236
mix3 extreme = 3.50848 (sig = 00222210) weight >=5 (4864), p-value = 0.888
bits per word = 64 (analyzing bits); min category p-value = 0.213
processed 6.1e+08 bytes in 1 seconds (0.6101 GB/s, 2.196 TB/h). Fri Feb 21 23:22:22 2025
p = 0.698
------
mix3 extreme = 1.78022 (sig = 00000002) weight 1 (16), p-value = 0.713
mix3 extreme = 2.64570 (sig = 10000001) weight 2 (112), p-value = 0.6
mix3 extreme = 3.55344 (sig = 20002010) weight 3 (448), p-value = 0.157
mix3 extreme = 3.15383 (sig = 11000102) weight 4 (1120), p-value = 0.836
mix3 extreme = 4.03858 (sig = 11121011) weight >=5 (4864), p-value = 0.23
bits per word = 64 (analyzing bits); min category p-value = 0.157
processed 7.03e+08 bytes in 1 seconds (0.7026 GB/s, 2.529 TB/h). Fri Feb 21 23:22:22 2025
p = 0.573
------
mix3 extreme = 1.83987 (sig = 00000002) weight 1 (16), p-value = 0.663
mix3 extreme = 2.43194 (sig = 00000110) weight 2 (112), p-value = 0.816
mix3 extreme = 3.40962 (sig = 20000022) weight 3 (448), p-value = 0.253
mix3 extreme = 3.02964 (sig = 02012200) weight 4 (1120), p-value = 0.936
mix3 extreme = 3.75207 (sig = 20222201) weight >=5 (4864), p-value = 0.574
bits per word = 64 (analyzing bits); min category p-value = 0.253
processed 8.5e+08 bytes in 1 seconds (0.8505 GB/s, 3.062 TB/h). Fri Feb 21 23:22:22 2025
p = 0.767
------
mix3 extreme = 2.03491 (sig = 00000002) weight 1 (16), p-value = 0.496
mix3 extreme = 2.48334 (sig = 00001002) weight 2 (112), p-value = 0.769
mix3 extreme = 3.15042 (sig = 10200002) weight 3 (448), p-value = 0.519
mix3 extreme = 3.19215 (sig = 21200001) weight 4 (1120), p-value = 0.795
mix3 extreme = 3.68390 (sig = 20222201) weight >=5 (4864), p-value = 0.673
bits per word = 64 (analyzing bits); min category p-value = 0.496
processed 1.02e+09 bytes in 1 seconds (1.017 GB/s, 3.661 TB/h). Fri Feb 21 23:22:22 2025
p = 0.967
------
mix3 extreme = 1.98184 (sig = 00000001) weight 1 (16), p-value = 0.541
mix3 extreme = 2.49397 (sig = 01001000) weight 2 (112), p-value = 0.759
mix3 extreme = 2.98386 (sig = 02000011) weight 3 (448), p-value = 0.721
mix3 extreme = 3.29022 (sig = 20200220) weight 4 (1120), p-value = 0.674
mix3 extreme = 4.30382 (sig = 11122011) weight >=5 (4864), p-value = 0.0784
bits per word = 64 (analyzing bits); min category p-value = 0.0784
processed 1.26e+09 bytes in 1 seconds (1.257 GB/s, 4.526 TB/h). Fri Feb 21 23:22:22 2025
p = 0.335
------
mix3 extreme = 2.33599 (sig = 00000001) weight 1 (16), p-value = 0.27
mix3 extreme = 2.45643 (sig = 00000110) weight 2 (112), p-value = 0.795
mix3 extreme = 2.84156 (sig = 00022100) weight 3 (448), p-value = 0.867
mix3 extreme = 3.61210 (sig = 01100202) weight 4 (1120), p-value = 0.288
mix3 extreme = 3.51573 (sig = 21101210) weight >=5 (4864), p-value = 0.882
bits per word = 64 (analyzing bits); min category p-value = 0.27
processed 1.52e+09 bytes in 1 seconds (1.516 GB/s, 5.458 TB/h). Fri Feb 21 23:22:22 2025
p = 0.793
------
mix3 extreme = 2.57590 (sig = 00000001) weight 1 (16), p-value = 0.149
mix3 extreme = 2.18056 (sig = 10000002) weight 2 (112), p-value = 0.964
mix3 extreme = 2.94943 (sig = 20102000) weight 3 (448), p-value = 0.76
mix3 extreme = 3.39387 (sig = 11010002) weight 4 (1120), p-value = 0.538
mix3 extreme = 3.85744 (sig = 22210212) weight >=5 (4864), p-value = 0.427
bits per word = 64 (analyzing bits); min category p-value = 0.149
processed 1.76e+09 bytes in 1 seconds (1.756 GB/s, 6.323 TB/h). Fri Feb 21 23:22:22 2025
p = 0.552
------
mix3 extreme = 2.64879 (sig = 00000001) weight 1 (16), p-value = 0.122
mix3 extreme = 2.69778 (sig = 10000002) weight 2 (112), p-value = 0.544
mix3 extreme = 3.15759 (sig = 20102000) weight 3 (448), p-value = 0.51
mix3 extreme = 3.41194 (sig = 11010002) weight 4 (1120), p-value = 0.515
mix3 extreme = 4.19052 (sig = 11122011) weight >=5 (4864), p-value = 0.127
bits per word = 64 (analyzing bits); min category p-value = 0.122
processed 2.02e+09 bytes in 1 seconds (2.015 GB/s, 7.255 TB/h). Fri Feb 21 23:22:22 2025
p = 0.477
------
mix3 extreme = 2.10150 (sig = 00000001) weight 1 (16), p-value = 0.44
mix3 extreme = 2.31937 (sig = 00100100) weight 2 (112), p-value = 0.9
mix3 extreme = 2.98427 (sig = 20102000) weight 3 (448), p-value = 0.721
mix3 extreme = 3.62790 (sig = 21200001) weight 4 (1120), p-value = 0.274
mix3 extreme = 3.47379 (sig = 22011100) weight >=5 (4864), p-value = 0.918
bits per word = 64 (analyzing bits); min category p-value = 0.274
processed 2.51e+09 bytes in 1 seconds (2.514 GB/s, 9.052 TB/h). Fri Feb 21 23:22:22 2025
p = 0.798
------
mix3 extreme = 2.20184 (sig = 00100000) weight 1 (16), p-value = 0.362
mix3 extreme = 2.44576 (sig = 00100100) weight 2 (112), p-value = 0.804
mix3 extreme = 2.66267 (sig = 10200002) weight 3 (448), p-value = 0.969
mix3 extreme = 3.76139 (sig = 21200001) weight 4 (1120), p-value = 0.172
mix3 extreme = 3.82048 (sig = 20222201) weight >=5 (4864), p-value = 0.477
bits per word = 64 (analyzing bits); min category p-value = 0.172
processed 3.01e+09 bytes in 2 seconds (1.507 GB/s, 5.425 TB/h). Fri Feb 21 23:22:23 2025
p = 0.612
------
mix3 extreme = 2.18426 (sig = 00100000) weight 1 (16), p-value = 0.375
mix3 extreme = 2.65062 (sig = 20002000) weight 2 (112), p-value = 0.595
mix3 extreme = 2.92917 (sig = 01100200) weight 3 (448), p-value = 0.782
mix3 extreme = 3.89526 (sig = 21200001) weight 4 (1120), p-value = 0.104
mix3 extreme = 3.78679 (sig = 20220022) weight >=5 (4864), p-value = 0.524
bits per word = 64 (analyzing bits); min category p-value = 0.104
processed 4.01e+09 bytes in 2 seconds (2.006 GB/s, 7.222 TB/h). Fri Feb 21 23:22:23 2025
p = 0.423
------
mix3 extreme = 2.46216 (sig = 20000000) weight 1 (16), p-value = 0.199
mix3 extreme = 2.30564 (sig = 20002000) weight 2 (112), p-value = 0.909
mix3 extreme = 2.97563 (sig = 00100220) weight 3 (448), p-value = 0.731
mix3 extreme = 3.70435 (sig = 20110001) weight 4 (1120), p-value = 0.211
mix3 extreme = 3.47865 (sig = 22100011) weight >=5 (4864), p-value = 0.914
bits per word = 64 (analyzing bits); min category p-value = 0.199
processed 5.01e+09 bytes in 2 seconds (2.505 GB/s, 9.019 TB/h). Fri Feb 21 23:22:23 2025
p = 0.671
------
mix3 extreme = 2.48402 (sig = 00100000) weight 1 (16), p-value = 0.189
mix3 extreme = 2.32620 (sig = 00000012) weight 2 (112), p-value = 0.896
mix3 extreme = 3.13986 (sig = 00120200) weight 3 (448), p-value = 0.531
mix3 extreme = 3.56935 (sig = 20110001) weight 4 (1120), p-value = 0.33
mix3 extreme = 3.62670 (sig = 01210201) weight >=5 (4864), p-value = 0.753
bits per word = 64 (analyzing bits); min category p-value = 0.189
processed 6.01e+09 bytes in 3 seconds (2.003 GB/s, 7.211 TB/h). Fri Feb 21 23:22:24 2025
p = 0.649
------
mix3 extreme = 2.07995 (sig = 00100000) weight 1 (16), p-value = 0.458
mix3 extreme = 2.14438 (sig = 00002010) weight 2 (112), p-value = 0.974
mix3 extreme = 3.12213 (sig = 00120200) weight 3 (448), p-value = 0.553
mix3 extreme = 3.59330 (sig = 20110001) weight 4 (1120), p-value = 0.306
mix3 extreme = 3.93490 (sig = 01210201) weight >=5 (4864), p-value = 0.333
bits per word = 64 (analyzing bits); min category p-value = 0.306
processed 7.01e+09 bytes in 3 seconds (2.336 GB/s, 8.409 TB/h). Fri Feb 21 23:22:24 2025
p = 0.839
------
mix3 extreme = 1.82939 (sig = 20000000) weight 1 (16), p-value = 0.672
mix3 extreme = 2.04212 (sig = 00002010) weight 2 (112), p-value = 0.991
mix3 extreme = 3.40400 (sig = 00120200) weight 3 (448), p-value = 0.257
mix3 extreme = 3.20336 (sig = 12020002) weight 4 (1120), p-value = 0.782
mix3 extreme = 4.26991 (sig = 01210201) weight >=5 (4864), p-value = 0.0907
bits per word = 64 (analyzing bits); min category p-value = 0.0907
processed 8.5e+09 bytes in 4 seconds (2.126 GB/s, 7.654 TB/h). Fri Feb 21 23:22:25 2025
p = 0.378
------
mix3 extreme = 1.82510 (sig = 00100000) weight 1 (16), p-value = 0.676
mix3 extreme = 2.69933 (sig = 00002020) weight 2 (112), p-value = 0.542
mix3 extreme = 2.78787 (sig = 02020200) weight 3 (448), p-value = 0.908
mix3 extreme = 3.06049 (sig = 00001122) weight 4 (1120), p-value = 0.916
mix3 extreme = 4.40242 (sig = 01210201) weight >=5 (4864), p-value = 0.0507
bits per word = 64 (analyzing bits); min category p-value = 0.0507
processed 1e+10 bytes in 4 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:25 2025
p = 0.229
------
mix3 extreme = 2.20665 (sig = 00100000) weight 1 (16), p-value = 0.358
mix3 extreme = 2.66682 (sig = 00002020) weight 2 (112), p-value = 0.577
mix3 extreme = 3.18173 (sig = 02200010) weight 3 (448), p-value = 0.481
mix3 extreme = 3.39020 (sig = 01202100) weight 4 (1120), p-value = 0.543
mix3 extreme = 3.99569 (sig = 01210201) weight >=5 (4864), p-value = 0.269
bits per word = 64 (analyzing bits); min category p-value = 0.269
processed 1.25e+10 bytes in 5 seconds (2.503 GB/s, 9.012 TB/h). Fri Feb 21 23:22:26 2025
p = 0.792
------
mix3 extreme = 1.60923 (sig = 00000002) weight 1 (16), p-value = 0.838
mix3 extreme = 2.73755 (sig = 02000001) weight 2 (112), p-value = 0.501
mix3 extreme = 3.23640 (sig = 00101200) weight 3 (448), p-value = 0.419
mix3 extreme = 3.31439 (sig = 21220000) weight 4 (1120), p-value = 0.643
mix3 extreme = 3.57996 (sig = 21001101) weight >=5 (4864), p-value = 0.812
bits per word = 64 (analyzing bits); min category p-value = 0.419
processed 1.5e+10 bytes in 6 seconds (2.502 GB/s, 9.008 TB/h). Fri Feb 21 23:22:27 2025
p = 0.934
------
mix3 extreme = 1.91785 (sig = 00000002) weight 1 (16), p-value = 0.596
mix3 extreme = 2.60874 (sig = 02000001) weight 2 (112), p-value = 0.64
mix3 extreme = 3.38156 (sig = 00101200) weight 3 (448), p-value = 0.276
mix3 extreme = 3.24044 (sig = 21220000) weight 4 (1120), p-value = 0.737
mix3 extreme = 3.69590 (sig = 12211201) weight >=5 (4864), p-value = 0.656
bits per word = 64 (analyzing bits); min category p-value = 0.276
processed 1.75e+10 bytes in 7 seconds (2.501 GB/s, 9.004 TB/h). Fri Feb 21 23:22:28 2025
p = 0.801
------
mix3 extreme = 1.80532 (sig = 00000001) weight 1 (16), p-value = 0.692
mix3 extreme = 2.54416 (sig = 00020010) weight 2 (112), p-value = 0.709
mix3 extreme = 3.15400 (sig = 00101200) weight 3 (448), p-value = 0.514
mix3 extreme = 3.33906 (sig = 21220000) weight 4 (1120), p-value = 0.61
mix3 extreme = 3.56547 (sig = 00012112) weight >=5 (4864), p-value = 0.829
bits per word = 64 (analyzing bits); min category p-value = 0.514
processed 2e+10 bytes in 8 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:29 2025
p = 0.973
------
mix3 extreme = 1.94039 (sig = 00000002) weight 1 (16), p-value = 0.577
mix3 extreme = 2.27077 (sig = 01010000) weight 2 (112), p-value = 0.928
mix3 extreme = 3.19342 (sig = 00120200) weight 3 (448), p-value = 0.468
mix3 extreme = 3.51655 (sig = 21220000) weight 4 (1120), p-value = 0.387
mix3 extreme = 3.56607 (sig = 20122012) weight >=5 (4864), p-value = 0.828
bits per word = 64 (analyzing bits); min category p-value = 0.387
processed 2.5e+10 bytes in 10 seconds (2.502 GB/s, 9.005 TB/h). Fri Feb 21 23:22:31 2025
p = 0.914
------
mix3 extreme = 1.89200 (sig = 01000000) weight 1 (16), p-value = 0.619
mix3 extreme = 2.24801 (sig = 01010000) weight 2 (112), p-value = 0.938
mix3 extreme = 2.88784 (sig = 02000210) weight 3 (448), p-value = 0.825
mix3 extreme = 3.34758 (sig = 21220000) weight 4 (1120), p-value = 0.599
mix3 extreme = 3.45248 (sig = 11200111) weight >=5 (4864), p-value = 0.933
bits per word = 64 (analyzing bits); min category p-value = 0.599
processed 3e+10 bytes in 12 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:33 2025
p = 0.99
------
mix3 extreme = 1.56396 (sig = 00000200) weight 1 (16), p-value = 0.865
mix3 extreme = 1.97760 (sig = 00000022) weight 2 (112), p-value = 0.996
mix3 extreme = 3.23027 (sig = 20021000) weight 3 (448), p-value = 0.426
mix3 extreme = 3.36856 (sig = 21021000) weight 4 (1120), p-value = 0.571
mix3 extreme = 4.27622 (sig = 20122012) weight >=5 (4864), p-value = 0.0883
bits per word = 64 (analyzing bits); min category p-value = 0.0883
processed 4e+10 bytes in 16 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:37 2025
p = 0.37
------
mix3 extreme = 1.89666 (sig = 00000100) weight 1 (16), p-value = 0.615
mix3 extreme = 2.70819 (sig = 20000010) weight 2 (112), p-value = 0.532
mix3 extreme = 3.11747 (sig = 20021000) weight 3 (448), p-value = 0.559
mix3 extreme = 2.83492 (sig = 21021000) weight 4 (1120), p-value = 0.994
mix3 extreme = 3.73280 (sig = 20122012) weight >=5 (4864), p-value = 0.602
bits per word = 64 (analyzing bits); min category p-value = 0.532
processed 5e+10 bytes in 20 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:41 2025
p = 0.978
------
mix3 extreme = 1.60871 (sig = 00000001) weight 1 (16), p-value = 0.838
mix3 extreme = 2.92820 (sig = 20000010) weight 2 (112), p-value = 0.318
mix3 extreme = 3.30722 (sig = 20021000) weight 3 (448), p-value = 0.344
mix3 extreme = 3.12534 (sig = 12100002) weight 4 (1120), p-value = 0.863
mix3 extreme = 3.90592 (sig = 21101011) weight >=5 (4864), p-value = 0.367
bits per word = 64 (analyzing bits); min category p-value = 0.318
processed 6e+10 bytes in 24 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:45 2025
p = 0.852
------
mix3 extreme = 1.48010 (sig = 02000000) weight 1 (16), p-value = 0.909
mix3 extreme = 2.48996 (sig = 20000010) weight 2 (112), p-value = 0.763
mix3 extreme = 3.17803 (sig = 20021000) weight 3 (448), p-value = 0.486
mix3 extreme = 3.17420 (sig = 20101010) weight 4 (1120), p-value = 0.814
mix3 extreme = 3.79968 (sig = 21101011) weight >=5 (4864), p-value = 0.506
bits per word = 64 (analyzing bits); min category p-value = 0.486
processed 7e+10 bytes in 28 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:49 2025
p = 0.964
------
mix3 extreme = 2.31418 (sig = 02000000) weight 1 (16), p-value = 0.284
mix3 extreme = 2.75073 (sig = 02000001) weight 2 (112), p-value = 0.487
mix3 extreme = 3.22184 (sig = 20021000) weight 3 (448), p-value = 0.435
mix3 extreme = 2.99913 (sig = 12100002) weight 4 (1120), p-value = 0.952
mix3 extreme = 4.17272 (sig = 21101011) weight >=5 (4864), p-value = 0.136
bits per word = 64 (analyzing bits); min category p-value = 0.136
processed 8.5e+10 bytes in 35 seconds (2.429 GB/s, 8.744 TB/h). Fri Feb 21 23:22:56 2025
p = 0.519
------
mix3 extreme = 2.96167 (sig = 02000000) weight 1 (16), p-value = 0.0478
mix3 extreme = 2.15955 (sig = 02000001) weight 2 (112), p-value = 0.97
mix3 extreme = 3.37730 (sig = 20021000) weight 3 (448), p-value = 0.28
mix3 extreme = 3.52700 (sig = 12100002) weight 4 (1120), p-value = 0.376
mix3 extreme = 4.04580 (sig = 21101011) weight >=5 (4864), p-value = 0.224
bits per word = 64 (analyzing bits); min category p-value = 0.0478
processed 1e+11 bytes in 41 seconds (2.439 GB/s, 8.781 TB/h). Fri Feb 21 23:23:02 2025
p = 0.217
------
mix3 extreme = 2.47196 (sig = 02000000) weight 1 (16), p-value = 0.195
mix3 extreme = 2.51373 (sig = 02000001) weight 2 (112), p-value = 0.74
mix3 extreme = 3.39366 (sig = 20021000) weight 3 (448), p-value = 0.266
mix3 extreme = 3.21456 (sig = 12100002) weight 4 (1120), p-value = 0.769
mix3 extreme = 3.77086 (sig = 12221021) weight >=5 (4864), p-value = 0.547
bits per word = 64 (analyzing bits); min category p-value = 0.195
processed 1.25e+11 bytes in 51 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:23:12 2025
p = 0.661
------
mix3 extreme = 2.48207 (sig = 02000000) weight 1 (16), p-value = 0.19
mix3 extreme = 2.29109 (sig = 20020000) weight 2 (112), p-value = 0.917
mix3 extreme = 3.01591 (sig = 20021000) weight 3 (448), p-value = 0.683
mix3 extreme = 3.36633 (sig = 20200101) weight 4 (1120), p-value = 0.574
mix3 extreme = 3.40749 (sig = 02222012) weight >=5 (4864), p-value = 0.959
bits per word = 64 (analyzing bits); min category p-value = 0.19
processed 1.5e+11 bytes in 61 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:23:22 2025
p = 0.651
------
mix3 extreme = 2.14788 (sig = 02000000) weight 1 (16), p-value = 0.403
mix3 extreme = 2.23188 (sig = 01000002) weight 2 (112), p-value = 0.945
mix3 extreme = 3.01619 (sig = 20100002) weight 3 (448), p-value = 0.683
mix3 extreme = 3.35531 (sig = 22020010) weight 4 (1120), p-value = 0.589
mix3 extreme = 3.90091 (sig = 20120120) weight >=5 (4864), p-value = 0.373
bits per word = 64 (analyzing bits); min category p-value = 0.373
processed 1.75e+11 bytes in 71 seconds (2.465 GB/s, 8.874 TB/h). Fri Feb 21 23:23:32 2025
p = 0.903
------
mix3 extreme = 2.56990 (sig = 00010000) weight 1 (16), p-value = 0.151
mix3 extreme = 2.15879 (sig = 20020000) weight 2 (112), p-value = 0.97
mix3 extreme = 3.11637 (sig = 20100002) weight 3 (448), p-value = 0.56
mix3 extreme = 3.75031 (sig = 20200101) weight 4 (1120), p-value = 0.179
mix3 extreme = 3.82089 (sig = 11022021) weight >=5 (4864), p-value = 0.476
bits per word = 64 (analyzing bits); min category p-value = 0.151
processed 2e+11 bytes in 81 seconds (2.469 GB/s, 8.889 TB/h). Fri Feb 21 23:23:42 2025
p = 0.559
------
mix3 extreme = 2.68286 (sig = 00010000) weight 1 (16), p-value = 0.111
mix3 extreme = 2.03612 (sig = 01000002) weight 2 (112), p-value = 0.992
mix3 extreme = 2.72508 (sig = 01110000) weight 3 (448), p-value = 0.944
mix3 extreme = 3.29533 (sig = 20200101) weight 4 (1120), p-value = 0.668
mix3 extreme = 3.94062 (sig = 11101011) weight >=5 (4864), p-value = 0.327
bits per word = 64 (analyzing bits); min category p-value = 0.111
processed 2.5e+11 bytes in 102 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:24:03 2025
p = 0.444
------
mix3 extreme = 3.02806 (sig = 00010000) weight 1 (16), p-value = 0.0387
mix3 extreme = 2.64241 (sig = 12000000) weight 2 (112), p-value = 0.604
mix3 extreme = 3.14107 (sig = 12001000) weight 3 (448), p-value = 0.53
mix3 extreme = 3.10292 (sig = 10202002) weight 4 (1120), p-value = 0.883
mix3 extreme = 3.78800 (sig = 11122221) weight >=5 (4864), p-value = 0.522
bits per word = 64 (analyzing bits); min category p-value = 0.0387
processed 3e+11 bytes in 122 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:24:23 2025
p = 0.179
------
mix3 extreme = 2.11373 (sig = 00010000) weight 1 (16), p-value = 0.43
mix3 extreme = 2.53673 (sig = 00010020) weight 2 (112), p-value = 0.716
mix3 extreme = 3.82566 (sig = 01000220) weight 3 (448), p-value = 0.0568
mix3 extreme = 3.94266 (sig = 22020010) weight 4 (1120), p-value = 0.0863
mix3 extreme = 3.38864 (sig = 02212120) weight >=5 (4864), p-value = 0.967
bits per word = 64 (analyzing bits); min category p-value = 0.0568
processed 4e+11 bytes in 163 seconds (2.454 GB/s, 8.834 TB/h). Fri Feb 21 23:25:04 2025
p = 0.253
------
mix3 extreme = 2.02087 (sig = 02000000) weight 1 (16), p-value = 0.507
mix3 extreme = 3.38282 (sig = 00010020) weight 2 (112), p-value = 0.0772
mix3 extreme = 3.77275 (sig = 01000220) weight 3 (448), p-value = 0.0698
mix3 extreme = 3.96045 (sig = 22020010) weight 4 (1120), p-value = 0.0804
mix3 extreme = 3.96698 (sig = 10212100) weight >=5 (4864), p-value = 0.298
bits per word = 64 (analyzing bits); min category p-value = 0.0698
processed 5e+11 bytes in 204 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:25:45 2025
p = 0.304
------
mix3 extreme = 2.11460 (sig = 02000000) weight 1 (16), p-value = 0.429
mix3 extreme = 3.45081 (sig = 00010020) weight 2 (112), p-value = 0.0607
mix3 extreme = 4.11176 (sig = 01000220) weight 3 (448), p-value = 0.0174
mix3 extreme = 3.90641 (sig = 22020010) weight 4 (1120), p-value = 0.0996
mix3 extreme = 3.86051 (sig = 10110212) weight >=5 (4864), p-value = 0.423
bits per word = 64 (analyzing bits); min category p-value = 0.0174
processed 6e+11 bytes in 244 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:26:25 2025
p = 0.0842
------
mix3 extreme = 2.19471 (sig = 02000000) weight 1 (16), p-value = 0.367
mix3 extreme = 3.42206 (sig = 00010020) weight 2 (112), p-value = 0.0673
mix3 extreme = 3.76615 (sig = 01000220) weight 3 (448), p-value = 0.0716
mix3 extreme = 3.59051 (sig = 22020010) weight 4 (1120), p-value = 0.309
mix3 extreme = 3.55018 (sig = 20012221) weight >=5 (4864), p-value = 0.846
bits per word = 64 (analyzing bits); min category p-value = 0.0673
processed 7e+11 bytes in 285 seconds (2.456 GB/s, 8.842 TB/h). Fri Feb 21 23:27:06 2025
p = 0.294
------
mix3 extreme = 2.08568 (sig = 02000000) weight 1 (16), p-value = 0.453
mix3 extreme = 2.70372 (sig = 00010020) weight 2 (112), p-value = 0.537
mix3 extreme = 3.67441 (sig = 20021000) weight 3 (448), p-value = 0.101
mix3 extreme = 3.40600 (sig = 22020010) weight 4 (1120), p-value = 0.522
mix3 extreme = 3.74169 (sig = 10110212) weight >=5 (4864), p-value = 0.589
bits per word = 64 (analyzing bits); min category p-value = 0.101
processed 8.5e+11 bytes in 346 seconds (2.457 GB/s, 8.844 TB/h). Fri Feb 21 23:28:07 2025
p = 0.414
------
mix3 extreme = 1.70943 (sig = 00000100) weight 1 (16), p-value = 0.768
mix3 extreme = 3.16917 (sig = 00010020) weight 2 (112), p-value = 0.157
mix3 extreme = 3.64671 (sig = 02000201) weight 3 (448), p-value = 0.112
mix3 extreme = 3.82262 (sig = 11010200) weight 4 (1120), p-value = 0.137
mix3 extreme = 3.64758 (sig = 00222021) weight >=5 (4864), p-value = 0.724
bits per word = 64 (analyzing bits); min category p-value = 0.112
processed 1e+12 bytes in 407 seconds (2.457 GB/s, 8.845 TB/h). Fri Feb 21 23:29:08 2025
p = 0.448
------
mix3 extreme = 1.76773 (sig = 20000000) weight 1 (16), p-value = 0.723
mix3 extreme = 3.14942 (sig = 00002020) weight 2 (112), p-value = 0.168
mix3 extreme = 3.45782 (sig = 02000201) weight 3 (448), p-value = 0.217
mix3 extreme = 3.68367 (sig = 11010200) weight 4 (1120), p-value = 0.227
mix3 extreme = 4.05225 (sig = 00222021) weight >=5 (4864), p-value = 0.219
bits per word = 64 (analyzing bits); min category p-value = 0.168
processed 1.25e+12 bytes in 509 seconds (2.456 GB/s, 8.841 TB/h). Fri Feb 21 23:30:50 2025
p = 0.6
------
mix3 extreme = 1.78446 (sig = 00000001) weight 1 (16), p-value = 0.709
mix3 extreme = 3.00030 (sig = 00002020) weight 2 (112), p-value = 0.261
mix3 extreme = 3.68239 (sig = 02000201) weight 3 (448), p-value = 0.0983
mix3 extreme = 3.38984 (sig = 11010020) weight 4 (1120), p-value = 0.543
mix3 extreme = 4.00734 (sig = 02221002) weight >=5 (4864), p-value = 0.258
bits per word = 64 (analyzing bits); min category p-value = 0.0983
processed 1.5e+12 bytes in 611 seconds (2.455 GB/s, 8.838 TB/h). Fri Feb 21 23:32:32 2025
p = 0.404
------
mix3 extreme = 1.49970 (sig = 02000000) weight 1 (16), p-value = 0.899
mix3 extreme = 2.64430 (sig = 20000020) weight 2 (112), p-value = 0.602
mix3 extreme = 3.85954 (sig = 02000201) weight 3 (448), p-value = 0.0496
mix3 extreme = 3.76326 (sig = 11010020) weight 4 (1120), p-value = 0.171
mix3 extreme = 3.91001 (sig = 00222021) weight >=5 (4864), p-value = 0.362
bits per word = 64 (analyzing bits); min category p-value = 0.0496
processed 1.75e+12 bytes in 713 seconds (2.454 GB/s, 8.836 TB/h). Fri Feb 21 23:34:14 2025
p = 0.225
------
mix3 extreme = 1.64313 (sig = 02000000) weight 1 (16), p-value = 0.816
mix3 extreme = 3.02382 (sig = 02100000) weight 2 (112), p-value = 0.244
mix3 extreme = 3.21441 (sig = 02000201) weight 3 (448), p-value = 0.443
mix3 extreme = 4.07811 (sig = 11010020) weight 4 (1120), p-value = 0.0496
mix3 extreme = 4.50737 (sig = 00222021) weight >=5 (4864), p-value = 0.0314
bits per word = 64 (analyzing bits); min category p-value = 0.0314
processed 2e+12 bytes in 815 seconds (2.454 GB/s, 8.834 TB/h). Fri Feb 21 23:35:56 2025
p = 0.148
------
mix3 extreme = 1.57884 (sig = 20000000) weight 1 (16), p-value = 0.857
mix3 extreme = 2.68908 (sig = 00001002) weight 2 (112), p-value = 0.553
mix3 extreme = 3.22275 (sig = 01000220) weight 3 (448), p-value = 0.434
mix3 extreme = 3.84110 (sig = 11010020) weight 4 (1120), p-value = 0.128
mix3 extreme = 3.89955 (sig = 10020112) weight >=5 (4864), p-value = 0.374
bits per word = 64 (analyzing bits); min category p-value = 0.128
processed 2.5e+12 bytes in 1.02e+03 seconds (2.456 GB/s, 8.841 TB/h). Fri Feb 21 23:39:19 2025
p = 0.496
------
mix3 extreme = 1.67736 (sig = 00001000) weight 1 (16), p-value = 0.792
mix3 extreme = 2.62488 (sig = 00001002) weight 2 (112), p-value = 0.623
mix3 extreme = 3.38419 (sig = 02002002) weight 3 (448), p-value = 0.274
mix3 extreme = 3.80757 (sig = 11010020) weight 4 (1120), p-value = 0.145
mix3 extreme = 3.95015 (sig = 00211221) weight >=5 (4864), p-value = 0.316
bits per word = 64 (analyzing bits); min category p-value = 0.145
processed 3e+12 bytes in 1.22e+03 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:42:45 2025
p = 0.544
------
mix3 extreme = 1.79534 (sig = 00002000) weight 1 (16), p-value = 0.701
mix3 extreme = 2.76965 (sig = 00001002) weight 2 (112), p-value = 0.468
mix3 extreme = 3.58785 (sig = 01000220) weight 3 (448), p-value = 0.139
mix3 extreme = 3.25341 (sig = 20200012) weight 4 (1120), p-value = 0.721
mix3 extreme = 3.65576 (sig = 12111122) weight >=5 (4864), p-value = 0.713
bits per word = 64 (analyzing bits); min category p-value = 0.139
processed 4e+12 bytes in 1.63e+03 seconds (2.448 GB/s, 8.813 TB/h). Fri Feb 21 23:49:35 2025
p = 0.526
------
```
------------------------------------------------------------
### `FIO_DEFINE_RANDOM128_FN`
The following are the tests for the built-in `FIO_DEFINE_RANDOM128_FN` macro using the deterministic PRNG (where the auto-reseeding `reseed_log` is set to `0`).
**Note**: setting `reseed_log` to any practical value (such as `31`) would improve randomness make the PRNG non-deterministic, improving the security of the result.
**The `PractRand` results**:
```txt
# ./tmp/rnd -p M128 | RNG_test stdin
RNG_test using PractRand version 0.95
RNG = RNG_stdin, seed = unknown
test set = core, folding = standard(unknown format)
rng=RNG_stdin, seed=unknown
length= 256 megabytes (2^28 bytes), time= 2.4 seconds
no anomalies in 217 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 megabytes (2^29 bytes), time= 5.0 seconds
no anomalies in 232 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 gigabyte (2^30 bytes), time= 10.1 seconds
no anomalies in 251 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 20.6 seconds
no anomalies in 269 test result(s)
rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 41.2 seconds
no anomalies in 283 test result(s)
rng=RNG_stdin, seed=unknown
length= 8 gigabytes (2^33 bytes), time= 83.9 seconds
no anomalies in 300 test result(s)
rng=RNG_stdin, seed=unknown
length= 16 gigabytes (2^34 bytes), time= 167 seconds
no anomalies in 315 test result(s)
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 330 seconds
no anomalies in 328 test result(s)
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 665 seconds
no anomalies in 344 test result(s)
rng=RNG_stdin, seed=unknown
length= 128 gigabytes (2^37 bytes), time= 1329 seconds
no anomalies in 359 test result(s)
rng=RNG_stdin, seed=unknown
length= 256 gigabytes (2^38 bytes), time= 2630 seconds
no anomalies in 372 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 gigabytes (2^39 bytes), time= 6846 seconds
no anomalies in 387 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 terabyte (2^40 bytes), time= 18633 seconds
no anomalies in 401 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 terabytes (2^41 bytes), time= 39061 seconds
no anomalies in 413 test result(s)
```
**The tests adopted from the `xoshiro` code base**:
```txt
# ./tmp/random m128
mix3 extreme = 1.90403 (sig = 00020000) weight 1 (16), p-value = 0.608
mix3 extreme = 2.78422 (sig = 01000001) weight 2 (112), p-value = 0.453
mix3 extreme = 2.98802 (sig = 00001220) weight 3 (448), p-value = 0.716
mix3 extreme = 3.51263 (sig = 22110000) weight 4 (1120), p-value = 0.392
mix3 extreme = 3.86128 (sig = 22110200) weight >=5 (4864), p-value = 0.422
bits per word = 64 (analyzing bits); min category p-value = 0.392
processed 1.11e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.917
------
mix3 extreme = 2.33512 (sig = 00020000) weight 1 (16), p-value = 0.271
mix3 extreme = 2.60631 (sig = 00000011) weight 2 (112), p-value = 0.643
mix3 extreme = 3.20941 (sig = 00001220) weight 3 (448), p-value = 0.449
mix3 extreme = 3.23491 (sig = 10000221) weight 4 (1120), p-value = 0.744
mix3 extreme = 3.98181 (sig = 12101200) weight >=5 (4864), p-value = 0.283
bits per word = 64 (analyzing bits); min category p-value = 0.271
processed 1.29e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.794
------
mix3 extreme = 2.09795 (sig = 00020000) weight 1 (16), p-value = 0.443
mix3 extreme = 2.59859 (sig = 00000011) weight 2 (112), p-value = 0.651
mix3 extreme = 3.16919 (sig = 00020012) weight 3 (448), p-value = 0.496
mix3 extreme = 3.32522 (sig = 01002210) weight 4 (1120), p-value = 0.628
mix3 extreme = 3.87995 (sig = 20211102) weight >=5 (4864), p-value = 0.398
bits per word = 64 (analyzing bits); min category p-value = 0.398
processed 1.66e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.921
------
mix3 extreme = 1.97959 (sig = 00020000) weight 1 (16), p-value = 0.543
mix3 extreme = 2.47936 (sig = 00000011) weight 2 (112), p-value = 0.773
mix3 extreme = 3.25629 (sig = 02000022) weight 3 (448), p-value = 0.397
mix3 extreme = 3.20819 (sig = 20001012) weight 4 (1120), p-value = 0.776
mix3 extreme = 3.85604 (sig = 21111101) weight >=5 (4864), p-value = 0.429
bits per word = 64 (analyzing bits); min category p-value = 0.397
processed 1.85e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.92
------
mix3 extreme = 2.14920 (sig = 00020000) weight 1 (16), p-value = 0.402
mix3 extreme = 2.58549 (sig = 00000011) weight 2 (112), p-value = 0.665
mix3 extreme = 2.97421 (sig = 02000022) weight 3 (448), p-value = 0.732
mix3 extreme = 2.92783 (sig = 00112001) weight 4 (1120), p-value = 0.978
mix3 extreme = 3.95789 (sig = 22110200) weight >=5 (4864), p-value = 0.308
bits per word = 64 (analyzing bits); min category p-value = 0.308
processed 2.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.841
------
mix3 extreme = 1.47529 (sig = 00001000) weight 1 (16), p-value = 0.911
mix3 extreme = 2.73432 (sig = 10000100) weight 2 (112), p-value = 0.505
mix3 extreme = 2.85412 (sig = 00220100) weight 3 (448), p-value = 0.856
mix3 extreme = 3.71866 (sig = 20200011) weight 4 (1120), p-value = 0.201
mix3 extreme = 3.82703 (sig = 12212021) weight >=5 (4864), p-value = 0.468
bits per word = 64 (analyzing bits); min category p-value = 0.201
processed 2.59e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.674
------
mix3 extreme = 1.48360 (sig = 20000000) weight 1 (16), p-value = 0.907
mix3 extreme = 2.46121 (sig = 00002002) weight 2 (112), p-value = 0.79
mix3 extreme = 2.87026 (sig = 20000201) weight 3 (448), p-value = 0.841
mix3 extreme = 3.86069 (sig = 20200011) weight 4 (1120), p-value = 0.119
mix3 extreme = 3.76155 (sig = 21201200) weight >=5 (4864), p-value = 0.56
bits per word = 64 (analyzing bits); min category p-value = 0.119
processed 3.14e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.469
------
mix3 extreme = 1.84311 (sig = 20000000) weight 1 (16), p-value = 0.661
mix3 extreme = 2.57296 (sig = 10001000) weight 2 (112), p-value = 0.679
mix3 extreme = 3.19014 (sig = 20000201) weight 3 (448), p-value = 0.471
mix3 extreme = 3.98985 (sig = 20200011) weight 4 (1120), p-value = 0.0714
mix3 extreme = 3.85549 (sig = 22121010) weight >=5 (4864), p-value = 0.43
bits per word = 64 (analyzing bits); min category p-value = 0.0714
processed 4.07e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.309
------
mix3 extreme = 1.41079 (sig = 20000000) weight 1 (16), p-value = 0.937
mix3 extreme = 3.04738 (sig = 10001000) weight 2 (112), p-value = 0.228
mix3 extreme = 3.74043 (sig = 00220100) weight 3 (448), p-value = 0.079
mix3 extreme = 3.82980 (sig = 00101210) weight 4 (1120), p-value = 0.134
mix3 extreme = 3.95613 (sig = 12012120) weight >=5 (4864), p-value = 0.31
bits per word = 64 (analyzing bits); min category p-value = 0.079
processed 5.18e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.337
------
mix3 extreme = 1.46023 (sig = 00020000) weight 1 (16), p-value = 0.917
mix3 extreme = 2.75344 (sig = 00200100) weight 2 (112), p-value = 0.484
mix3 extreme = 3.35270 (sig = 20000201) weight 3 (448), p-value = 0.301
mix3 extreme = 3.74339 (sig = 02102100) weight 4 (1120), p-value = 0.184
mix3 extreme = 3.75002 (sig = 12012120) weight >=5 (4864), p-value = 0.577
bits per word = 64 (analyzing bits); min category p-value = 0.184
processed 6.1e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.638
------
mix3 extreme = 1.50237 (sig = 00000020) weight 1 (16), p-value = 0.898
mix3 extreme = 2.42220 (sig = 00200100) weight 2 (112), p-value = 0.825
mix3 extreme = 2.96916 (sig = 20000201) weight 3 (448), p-value = 0.738
mix3 extreme = 3.78954 (sig = 02102100) weight 4 (1120), p-value = 0.156
mix3 extreme = 3.66917 (sig = 10010121) weight >=5 (4864), p-value = 0.694
bits per word = 64 (analyzing bits); min category p-value = 0.156
processed 7.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.571
------
mix3 extreme = 1.78774 (sig = 00000020) weight 1 (16), p-value = 0.707
mix3 extreme = 2.70149 (sig = 00010100) weight 2 (112), p-value = 0.54
mix3 extreme = 3.27303 (sig = 01120000) weight 3 (448), p-value = 0.379
mix3 extreme = 4.21573 (sig = 02102100) weight 4 (1120), p-value = 0.0275
mix3 extreme = 3.70578 (sig = 12100210) weight >=5 (4864), p-value = 0.641
bits per word = 64 (analyzing bits); min category p-value = 0.0275
processed 8.5e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.13
------
mix3 extreme = 2.31948 (sig = 10000000) weight 1 (16), p-value = 0.281
mix3 extreme = 2.44934 (sig = 00010100) weight 2 (112), p-value = 0.801
mix3 extreme = 3.53130 (sig = 01120000) weight 3 (448), p-value = 0.169
mix3 extreme = 4.28717 (sig = 02102100) weight 4 (1120), p-value = 0.0201
mix3 extreme = 3.66277 (sig = 12100210) weight >=5 (4864), p-value = 0.703
bits per word = 64 (analyzing bits); min category p-value = 0.0201
processed 1.02e+09 bytes in 1 seconds (1.017 GB/s, 3.661 TB/h). Fri Feb 21 23:22:46 2025
p = 0.0964
------
mix3 extreme = 2.60876 (sig = 00000020) weight 1 (16), p-value = 0.136
mix3 extreme = 2.67150 (sig = 22000000) weight 2 (112), p-value = 0.572
mix3 extreme = 3.39462 (sig = 01120000) weight 3 (448), p-value = 0.265
mix3 extreme = 3.62549 (sig = 02200120) weight 4 (1120), p-value = 0.276
mix3 extreme = 3.63707 (sig = 12100021) weight >=5 (4864), p-value = 0.739
bits per word = 64 (analyzing bits); min category p-value = 0.136
processed 1.26e+09 bytes in 1 seconds (1.257 GB/s, 4.526 TB/h). Fri Feb 21 23:22:46 2025
p = 0.518
------
mix3 extreme = 2.15590 (sig = 00000020) weight 1 (16), p-value = 0.397
mix3 extreme = 2.80570 (sig = 00001010) weight 2 (112), p-value = 0.431
mix3 extreme = 3.40481 (sig = 02020001) weight 3 (448), p-value = 0.257
mix3 extreme = 3.36693 (sig = 01100012) weight 4 (1120), p-value = 0.573
mix3 extreme = 3.51384 (sig = 12012120) weight >=5 (4864), p-value = 0.883
bits per word = 64 (analyzing bits); min category p-value = 0.257
processed 1.52e+09 bytes in 1 seconds (1.516 GB/s, 5.458 TB/h). Fri Feb 21 23:22:46 2025
p = 0.773
------
mix3 extreme = 1.90047 (sig = 00000020) weight 1 (16), p-value = 0.611
mix3 extreme = 3.23402 (sig = 00001010) weight 2 (112), p-value = 0.128
mix3 extreme = 2.96881 (sig = 20001001) weight 3 (448), p-value = 0.739
mix3 extreme = 3.52470 (sig = 01012002) weight 4 (1120), p-value = 0.378
mix3 extreme = 3.72569 (sig = 22111101) weight >=5 (4864), p-value = 0.612
bits per word = 64 (analyzing bits); min category p-value = 0.128
processed 1.76e+09 bytes in 1 seconds (1.756 GB/s, 6.323 TB/h). Fri Feb 21 23:22:46 2025
p = 0.495
------
mix3 extreme = 1.57279 (sig = 00000010) weight 1 (16), p-value = 0.86
mix3 extreme = 2.81387 (sig = 00001010) weight 2 (112), p-value = 0.423
mix3 extreme = 3.18715 (sig = 02002010) weight 3 (448), p-value = 0.475
mix3 extreme = 3.33070 (sig = 00101120) weight 4 (1120), p-value = 0.621
mix3 extreme = 4.16820 (sig = 20122202) weight >=5 (4864), p-value = 0.139
bits per word = 64 (analyzing bits); min category p-value = 0.139
processed 2.02e+09 bytes in 1 seconds (2.015 GB/s, 7.255 TB/h). Fri Feb 21 23:22:46 2025
p = 0.526
------
mix3 extreme = 1.53157 (sig = 00000020) weight 1 (16), p-value = 0.883
mix3 extreme = 2.71599 (sig = 00001010) weight 2 (112), p-value = 0.524
mix3 extreme = 3.16021 (sig = 02002010) weight 3 (448), p-value = 0.507
mix3 extreme = 3.26824 (sig = 00101120) weight 4 (1120), p-value = 0.703
mix3 extreme = 4.01251 (sig = 22021002) weight >=5 (4864), p-value = 0.253
bits per word = 64 (analyzing bits); min category p-value = 0.253
processed 2.51e+09 bytes in 1 seconds (2.514 GB/s, 9.052 TB/h). Fri Feb 21 23:22:46 2025
p = 0.768
------
mix3 extreme = 1.74122 (sig = 00000020) weight 1 (16), p-value = 0.744
mix3 extreme = 2.86200 (sig = 00010100) weight 2 (112), p-value = 0.377
mix3 extreme = 3.41797 (sig = 02002010) weight 3 (448), p-value = 0.246
mix3 extreme = 3.48775 (sig = 00101120) weight 4 (1120), p-value = 0.421
mix3 extreme = 4.32378 (sig = 22021002) weight >=5 (4864), p-value = 0.0719
bits per word = 64 (analyzing bits); min category p-value = 0.0719
processed 3.01e+09 bytes in 1 seconds (3.014 GB/s, 10.85 TB/h). Fri Feb 21 23:22:46 2025
p = 0.311
------
mix3 extreme = 1.61472 (sig = 00000020) weight 1 (16), p-value = 0.835
mix3 extreme = 3.22143 (sig = 00010100) weight 2 (112), p-value = 0.133
mix3 extreme = 3.34281 (sig = 02002010) weight 3 (448), p-value = 0.31
mix3 extreme = 3.41424 (sig = 10011100) weight 4 (1120), p-value = 0.512
mix3 extreme = 3.68273 (sig = 00122011) weight >=5 (4864), p-value = 0.675
bits per word = 64 (analyzing bits); min category p-value = 0.133
processed 4.01e+09 bytes in 1 seconds (4.012 GB/s, 14.44 TB/h). Fri Feb 21 23:22:46 2025
p = 0.511
------
mix3 extreme = 2.08971 (sig = 00000020) weight 1 (16), p-value = 0.45
mix3 extreme = 3.03226 (sig = 00010100) weight 2 (112), p-value = 0.238
mix3 extreme = 3.22814 (sig = 00010102) weight 3 (448), p-value = 0.428
mix3 extreme = 3.27577 (sig = 10011100) weight 4 (1120), p-value = 0.693
mix3 extreme = 3.67641 (sig = 12012120) weight >=5 (4864), p-value = 0.684
bits per word = 64 (analyzing bits); min category p-value = 0.238
processed 5.01e+09 bytes in 2 seconds (2.505 GB/s, 9.019 TB/h). Fri Feb 21 23:22:47 2025
p = 0.744
------
mix3 extreme = 1.91894 (sig = 10000000) weight 1 (16), p-value = 0.595
mix3 extreme = 2.77625 (sig = 00010100) weight 2 (112), p-value = 0.461
mix3 extreme = 3.06382 (sig = 00011001) weight 3 (448), p-value = 0.625
mix3 extreme = 3.14390 (sig = 21020200) weight 4 (1120), p-value = 0.846
mix3 extreme = 3.59615 (sig = 00122011) weight >=5 (4864), p-value = 0.792
bits per word = 64 (analyzing bits); min category p-value = 0.461
processed 6.01e+09 bytes in 2 seconds (3.004 GB/s, 10.82 TB/h). Fri Feb 21 23:22:47 2025
p = 0.954
------
mix3 extreme = 1.86597 (sig = 00000020) weight 1 (16), p-value = 0.641
mix3 extreme = 2.61078 (sig = 00010100) weight 2 (112), p-value = 0.638
mix3 extreme = 3.42404 (sig = 00011001) weight 3 (448), p-value = 0.242
mix3 extreme = 3.40741 (sig = 00020122) weight 4 (1120), p-value = 0.52
mix3 extreme = 3.89941 (sig = 00122011) weight >=5 (4864), p-value = 0.374
bits per word = 64 (analyzing bits); min category p-value = 0.242
processed 7.01e+09 bytes in 2 seconds (3.504 GB/s, 12.61 TB/h). Fri Feb 21 23:22:47 2025
p = 0.749
------
mix3 extreme = 1.73399 (sig = 00000020) weight 1 (16), p-value = 0.75
mix3 extreme = 2.96016 (sig = 20000200) weight 2 (112), p-value = 0.292
mix3 extreme = 3.09543 (sig = 02000022) weight 3 (448), p-value = 0.586
mix3 extreme = 3.42588 (sig = 10101001) weight 4 (1120), p-value = 0.497
mix3 extreme = 3.66808 (sig = 10110201) weight >=5 (4864), p-value = 0.695
bits per word = 64 (analyzing bits); min category p-value = 0.292
processed 8.5e+09 bytes in 3 seconds (2.835 GB/s, 10.21 TB/h). Fri Feb 21 23:22:48 2025
p = 0.822
------
mix3 extreme = 1.52914 (sig = 00200000) weight 1 (16), p-value = 0.885
mix3 extreme = 3.81757 (sig = 20000200) weight 2 (112), p-value = 0.015
mix3 extreme = 3.07953 (sig = 02201000) weight 3 (448), p-value = 0.605
mix3 extreme = 3.26619 (sig = 01102001) weight 4 (1120), p-value = 0.705
mix3 extreme = 3.73963 (sig = 02122020) weight >=5 (4864), p-value = 0.592
bits per word = 64 (analyzing bits); min category p-value = 0.015
processed 1e+10 bytes in 3 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:48 2025
p = 0.0727
------
mix3 extreme = 2.00898 (sig = 00020000) weight 1 (16), p-value = 0.518
mix3 extreme = 2.73872 (sig = 20000200) weight 2 (112), p-value = 0.5
mix3 extreme = 2.89865 (sig = 22000200) weight 3 (448), p-value = 0.814
mix3 extreme = 3.51139 (sig = 10002220) weight 4 (1120), p-value = 0.393
mix3 extreme = 3.46747 (sig = 21020120) weight >=5 (4864), p-value = 0.922
bits per word = 64 (analyzing bits); min category p-value = 0.393
processed 1.25e+10 bytes in 4 seconds (3.129 GB/s, 11.27 TB/h). Fri Feb 21 23:22:49 2025
p = 0.918
------
mix3 extreme = 2.54743 (sig = 00020000) weight 1 (16), p-value = 0.16
mix3 extreme = 2.66976 (sig = 00020200) weight 2 (112), p-value = 0.574
mix3 extreme = 3.21302 (sig = 02020010) weight 3 (448), p-value = 0.445
mix3 extreme = 3.17616 (sig = 10002220) weight 4 (1120), p-value = 0.812
mix3 extreme = 4.08852 (sig = 00122011) weight >=5 (4864), p-value = 0.19
bits per word = 64 (analyzing bits); min category p-value = 0.16
processed 1.5e+10 bytes in 5 seconds (3.003 GB/s, 10.81 TB/h). Fri Feb 21 23:22:50 2025
p = 0.582
------
mix3 extreme = 2.32339 (sig = 00020000) weight 1 (16), p-value = 0.278
mix3 extreme = 2.41362 (sig = 00001010) weight 2 (112), p-value = 0.832
mix3 extreme = 3.50336 (sig = 02020010) weight 3 (448), p-value = 0.186
mix3 extreme = 3.13576 (sig = 20221000) weight 4 (1120), p-value = 0.854
mix3 extreme = 4.28795 (sig = 00122011) weight >=5 (4864), p-value = 0.084
bits per word = 64 (analyzing bits); min category p-value = 0.084
processed 1.75e+10 bytes in 5 seconds (3.502 GB/s, 12.61 TB/h). Fri Feb 21 23:22:50 2025
p = 0.355
------
mix3 extreme = 2.55481 (sig = 00020000) weight 1 (16), p-value = 0.157
mix3 extreme = 2.72892 (sig = 00001010) weight 2 (112), p-value = 0.51
mix3 extreme = 3.28466 (sig = 11000001) weight 3 (448), p-value = 0.367
mix3 extreme = 3.42088 (sig = 01020102) weight 4 (1120), p-value = 0.503
mix3 extreme = 4.05525 (sig = 00122011) weight >=5 (4864), p-value = 0.216
bits per word = 64 (analyzing bits); min category p-value = 0.157
processed 2e+10 bytes in 6 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:51 2025
p = 0.575
------
mix3 extreme = 2.46488 (sig = 00020000) weight 1 (16), p-value = 0.198
mix3 extreme = 2.44365 (sig = 20000200) weight 2 (112), p-value = 0.806
mix3 extreme = 3.31277 (sig = 02020010) weight 3 (448), p-value = 0.339
mix3 extreme = 3.81631 (sig = 01020102) weight 4 (1120), p-value = 0.141
mix3 extreme = 4.12544 (sig = 00122011) weight >=5 (4864), p-value = 0.165
bits per word = 64 (analyzing bits); min category p-value = 0.141
processed 2.5e+10 bytes in 8 seconds (3.127 GB/s, 11.26 TB/h). Fri Feb 21 23:22:53 2025
p = 0.532
------
mix3 extreme = 2.18715 (sig = 00020000) weight 1 (16), p-value = 0.373
mix3 extreme = 2.10006 (sig = 20000020) weight 2 (112), p-value = 0.983
mix3 extreme = 3.08063 (sig = 00210002) weight 3 (448), p-value = 0.604
mix3 extreme = 3.66924 (sig = 02110010) weight 4 (1120), p-value = 0.239
mix3 extreme = 4.35154 (sig = 00122011) weight >=5 (4864), p-value = 0.0636
bits per word = 64 (analyzing bits); min category p-value = 0.0636
processed 3e+10 bytes in 9 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:54 2025
p = 0.28
------
mix3 extreme = 1.43572 (sig = 00020000) weight 1 (16), p-value = 0.927
mix3 extreme = 2.41760 (sig = 02000020) weight 2 (112), p-value = 0.829
mix3 extreme = 3.16026 (sig = 02100020) weight 3 (448), p-value = 0.507
mix3 extreme = 3.44650 (sig = 02220002) weight 4 (1120), p-value = 0.471
mix3 extreme = 4.35459 (sig = 00221220) weight >=5 (4864), p-value = 0.0628
bits per word = 64 (analyzing bits); min category p-value = 0.0628
processed 4e+10 bytes in 12 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:57 2025
p = 0.277
------
mix3 extreme = 1.73103 (sig = 02000000) weight 1 (16), p-value = 0.752
mix3 extreme = 2.59948 (sig = 02000020) weight 2 (112), p-value = 0.65
mix3 extreme = 3.24793 (sig = 02020010) weight 3 (448), p-value = 0.406
mix3 extreme = 3.18550 (sig = 00022021) weight 4 (1120), p-value = 0.802
mix3 extreme = 4.36307 (sig = 00122011) weight >=5 (4864), p-value = 0.0605
bits per word = 64 (analyzing bits); min category p-value = 0.0605
processed 5e+10 bytes in 15 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:00 2025
p = 0.268
------
mix3 extreme = 1.69245 (sig = 02000000) weight 1 (16), p-value = 0.781
mix3 extreme = 2.90012 (sig = 02000020) weight 2 (112), p-value = 0.342
mix3 extreme = 3.04010 (sig = 02100020) weight 3 (448), p-value = 0.654
mix3 extreme = 3.18726 (sig = 11012000) weight 4 (1120), p-value = 0.8
mix3 extreme = 4.13605 (sig = 00122011) weight >=5 (4864), p-value = 0.158
bits per word = 64 (analyzing bits); min category p-value = 0.158
processed 6e+10 bytes in 18 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:03 2025
p = 0.577
------
mix3 extreme = 2.02538 (sig = 02000000) weight 1 (16), p-value = 0.504
mix3 extreme = 2.69836 (sig = 20000020) weight 2 (112), p-value = 0.543
mix3 extreme = 2.74898 (sig = 00200110) weight 3 (448), p-value = 0.932
mix3 extreme = 3.09844 (sig = 11012000) weight 4 (1120), p-value = 0.887
mix3 extreme = 4.04079 (sig = 20022201) weight >=5 (4864), p-value = 0.228
bits per word = 64 (analyzing bits); min category p-value = 0.228
processed 7e+10 bytes in 21 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:06 2025
p = 0.726
------
mix3 extreme = 2.00762 (sig = 00020000) weight 1 (16), p-value = 0.519
mix3 extreme = 2.67485 (sig = 20000020) weight 2 (112), p-value = 0.569
mix3 extreme = 2.98340 (sig = 00110100) weight 3 (448), p-value = 0.722
mix3 extreme = 3.30873 (sig = 12210000) weight 4 (1120), p-value = 0.65
mix3 extreme = 4.04171 (sig = 20022201) weight >=5 (4864), p-value = 0.227
bits per word = 64 (analyzing bits); min category p-value = 0.227
processed 8.5e+10 bytes in 25 seconds (3.4 GB/s, 12.24 TB/h). Fri Feb 21 23:23:10 2025
p = 0.725
------
mix3 extreme = 1.98503 (sig = 00100000) weight 1 (16), p-value = 0.538
mix3 extreme = 2.83929 (sig = 20000020) weight 2 (112), p-value = 0.398
mix3 extreme = 3.24294 (sig = 00110100) weight 3 (448), p-value = 0.412
mix3 extreme = 3.26307 (sig = 00022012) weight 4 (1120), p-value = 0.709
mix3 extreme = 3.82851 (sig = 20212120) weight >=5 (4864), p-value = 0.466
bits per word = 64 (analyzing bits); min category p-value = 0.398
processed 1e+11 bytes in 30 seconds (3.333 GB/s, 12 TB/h). Fri Feb 21 23:23:15 2025
p = 0.921
------
mix3 extreme = 1.80847 (sig = 00000001) weight 1 (16), p-value = 0.69
mix3 extreme = 2.36679 (sig = 00001020) weight 2 (112), p-value = 0.868
mix3 extreme = 3.12962 (sig = 00110100) weight 3 (448), p-value = 0.544
mix3 extreme = 3.41207 (sig = 00022012) weight 4 (1120), p-value = 0.514
mix3 extreme = 3.50238 (sig = 01120021) weight >=5 (4864), p-value = 0.894
bits per word = 64 (analyzing bits); min category p-value = 0.514
processed 1.25e+11 bytes in 37 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:23:22 2025
p = 0.973
------
mix3 extreme = 1.61617 (sig = 01000000) weight 1 (16), p-value = 0.834
mix3 extreme = 2.75953 (sig = 10002000) weight 2 (112), p-value = 0.478
mix3 extreme = 3.01768 (sig = 22000200) weight 3 (448), p-value = 0.681
mix3 extreme = 3.12351 (sig = 20120020) weight 4 (1120), p-value = 0.865
mix3 extreme = 3.88895 (sig = 01201202) weight >=5 (4864), p-value = 0.387
bits per word = 64 (analyzing bits); min category p-value = 0.387
processed 1.5e+11 bytes in 45 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:30 2025
p = 0.914
------
mix3 extreme = 1.77082 (sig = 10000000) weight 1 (16), p-value = 0.721
mix3 extreme = 3.16452 (sig = 20000020) weight 2 (112), p-value = 0.16
mix3 extreme = 3.33010 (sig = 22000200) weight 3 (448), p-value = 0.322
mix3 extreme = 3.30032 (sig = 10202001) weight 4 (1120), p-value = 0.661
mix3 extreme = 3.71439 (sig = 01201202) weight >=5 (4864), p-value = 0.629
bits per word = 64 (analyzing bits); min category p-value = 0.16
processed 1.75e+11 bytes in 52 seconds (3.366 GB/s, 12.12 TB/h). Fri Feb 21 23:23:37 2025
p = 0.581
------
mix3 extreme = 1.63286 (sig = 10000000) weight 1 (16), p-value = 0.823
mix3 extreme = 3.04031 (sig = 20000020) weight 2 (112), p-value = 0.233
mix3 extreme = 3.07386 (sig = 00020120) weight 3 (448), p-value = 0.612
mix3 extreme = 3.25670 (sig = 20200101) weight 4 (1120), p-value = 0.717
mix3 extreme = 3.86559 (sig = 02121212) weight >=5 (4864), p-value = 0.417
bits per word = 64 (analyzing bits); min category p-value = 0.233
processed 2e+11 bytes in 59 seconds (3.39 GB/s, 12.2 TB/h). Fri Feb 21 23:23:44 2025
p = 0.734
------
mix3 extreme = 1.16364 (sig = 00000010) weight 1 (16), p-value = 0.989
mix3 extreme = 3.24185 (sig = 20000020) weight 2 (112), p-value = 0.125
mix3 extreme = 3.33043 (sig = 00020120) weight 3 (448), p-value = 0.322
mix3 extreme = 3.08521 (sig = 01100011) weight 4 (1120), p-value = 0.898
mix3 extreme = 3.38515 (sig = 20222110) weight >=5 (4864), p-value = 0.969
bits per word = 64 (analyzing bits); min category p-value = 0.125
processed 2.5e+11 bytes in 74 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:23:59 2025
p = 0.486
------
mix3 extreme = 1.51793 (sig = 02000000) weight 1 (16), p-value = 0.89
mix3 extreme = 3.13632 (sig = 02000020) weight 2 (112), p-value = 0.175
mix3 extreme = 4.30962 (sig = 00020120) weight 3 (448), p-value = 0.0073
mix3 extreme = 3.03490 (sig = 01000212) weight 4 (1120), p-value = 0.933
mix3 extreme = 3.69122 (sig = 12111112) weight >=5 (4864), p-value = 0.662
bits per word = 64 (analyzing bits); min category p-value = 0.0073
processed 3e+11 bytes in 89 seconds (3.371 GB/s, 12.14 TB/h). Fri Feb 21 23:24:14 2025
p = 0.036
------
mix3 extreme = 1.90308 (sig = 00002000) weight 1 (16), p-value = 0.609
mix3 extreme = 2.65558 (sig = 00022000) weight 2 (112), p-value = 0.589
mix3 extreme = 3.65368 (sig = 00020120) weight 3 (448), p-value = 0.109
mix3 extreme = 3.49934 (sig = 01000212) weight 4 (1120), p-value = 0.407
mix3 extreme = 3.96134 (sig = 12111112) weight >=5 (4864), p-value = 0.304
bits per word = 64 (analyzing bits); min category p-value = 0.109
processed 4e+11 bytes in 119 seconds (3.361 GB/s, 12.1 TB/h). Fri Feb 21 23:24:44 2025
p = 0.44
------
mix3 extreme = 2.01103 (sig = 00000010) weight 1 (16), p-value = 0.516
mix3 extreme = 2.75092 (sig = 20000020) weight 2 (112), p-value = 0.487
mix3 extreme = 3.58533 (sig = 00020120) weight 3 (448), p-value = 0.14
mix3 extreme = 3.00752 (sig = 00101101) weight 4 (1120), p-value = 0.948
mix3 extreme = 3.78108 (sig = 20222110) weight >=5 (4864), p-value = 0.532
bits per word = 64 (analyzing bits); min category p-value = 0.14
processed 5e+11 bytes in 148 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:25:13 2025
p = 0.53
------
mix3 extreme = 2.46075 (sig = 00000010) weight 1 (16), p-value = 0.2
mix3 extreme = 3.05595 (sig = 10001000) weight 2 (112), p-value = 0.222
mix3 extreme = 3.57444 (sig = 00020120) weight 3 (448), p-value = 0.146
mix3 extreme = 3.08460 (sig = 02100120) weight 4 (1120), p-value = 0.898
mix3 extreme = 3.57242 (sig = 21020202) weight >=5 (4864), p-value = 0.821
bits per word = 64 (analyzing bits); min category p-value = 0.146
processed 6e+11 bytes in 178 seconds (3.371 GB/s, 12.14 TB/h). Fri Feb 21 23:25:43 2025
p = 0.544
------
mix3 extreme = 2.39889 (sig = 00010000) weight 1 (16), p-value = 0.233
mix3 extreme = 3.05936 (sig = 20000020) weight 2 (112), p-value = 0.22
mix3 extreme = 3.35611 (sig = 00020120) weight 3 (448), p-value = 0.298
mix3 extreme = 3.06385 (sig = 12002002) weight 4 (1120), p-value = 0.914
mix3 extreme = 3.77731 (sig = 21020202) weight >=5 (4864), p-value = 0.538
bits per word = 64 (analyzing bits); min category p-value = 0.22
processed 7e+11 bytes in 207 seconds (3.382 GB/s, 12.17 TB/h). Fri Feb 21 23:26:12 2025
p = 0.712
------
mix3 extreme = 2.44670 (sig = 00010000) weight 1 (16), p-value = 0.207
mix3 extreme = 3.17448 (sig = 00101000) weight 2 (112), p-value = 0.155
mix3 extreme = 3.94606 (sig = 00020120) weight 3 (448), p-value = 0.035
mix3 extreme = 3.39578 (sig = 12002002) weight 4 (1120), p-value = 0.535
mix3 extreme = 3.46995 (sig = 22000212) weight >=5 (4864), p-value = 0.921
bits per word = 64 (analyzing bits); min category p-value = 0.035
processed 8.5e+11 bytes in 252 seconds (3.373 GB/s, 12.14 TB/h). Fri Feb 21 23:26:57 2025
p = 0.163
------
mix3 extreme = 2.60423 (sig = 00000200) weight 1 (16), p-value = 0.138
mix3 extreme = 4.14356 (sig = 02000020) weight 2 (112), p-value = 0.00382
mix3 extreme = 3.30244 (sig = 00210010) weight 3 (448), p-value = 0.349
mix3 extreme = 2.92058 (sig = 00111100) weight 4 (1120), p-value = 0.98
mix3 extreme = 3.64298 (sig = 21221022) weight >=5 (4864), p-value = 0.73
bits per word = 64 (analyzing bits); min category p-value = 0.00382
processed 1e+12 bytes in 296 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:27:41 2025
p = 0.019
------
mix3 extreme = 2.39411 (sig = 00000200) weight 1 (16), p-value = 0.236
mix3 extreme = 3.86207 (sig = 02000020) weight 2 (112), p-value = 0.0125
mix3 extreme = 3.43732 (sig = 00210010) weight 3 (448), p-value = 0.231
mix3 extreme = 3.30612 (sig = 10010102) weight 4 (1120), p-value = 0.654
mix3 extreme = 3.69570 (sig = 02122102) weight >=5 (4864), p-value = 0.656
bits per word = 64 (analyzing bits); min category p-value = 0.0125
processed 1.25e+12 bytes in 370 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:28:55 2025
p = 0.061
------
mix3 extreme = 1.91917 (sig = 00000200) weight 1 (16), p-value = 0.595
mix3 extreme = 2.88512 (sig = 10001000) weight 2 (112), p-value = 0.355
mix3 extreme = 3.79690 (sig = 00210010) weight 3 (448), p-value = 0.0635
mix3 extreme = 3.81192 (sig = 10010102) weight 4 (1120), p-value = 0.143
mix3 extreme = 3.94746 (sig = 20221222) weight >=5 (4864), p-value = 0.319
bits per word = 64 (analyzing bits); min category p-value = 0.0635
processed 1.5e+12 bytes in 444 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:30:09 2025
p = 0.28
------
mix3 extreme = 1.91653 (sig = 00000200) weight 1 (16), p-value = 0.598
mix3 extreme = 3.17062 (sig = 02000020) weight 2 (112), p-value = 0.157
mix3 extreme = 3.50267 (sig = 00210010) weight 3 (448), p-value = 0.186
mix3 extreme = 4.12447 (sig = 10010102) weight 4 (1120), p-value = 0.0408
mix3 extreme = 4.27098 (sig = 20221222) weight >=5 (4864), p-value = 0.0903
bits per word = 64 (analyzing bits); min category p-value = 0.0408
processed 1.75e+12 bytes in 518 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:31:23 2025
p = 0.188
------
mix3 extreme = 1.81906 (sig = 00000200) weight 1 (16), p-value = 0.681
mix3 extreme = 3.06798 (sig = 10100000) weight 2 (112), p-value = 0.215
mix3 extreme = 3.71332 (sig = 02020200) weight 3 (448), p-value = 0.0876
mix3 extreme = 4.23944 (sig = 10010102) weight 4 (1120), p-value = 0.0248
mix3 extreme = 4.44624 (sig = 20221222) weight >=5 (4864), p-value = 0.0416
bits per word = 64 (analyzing bits); min category p-value = 0.0248
processed 2e+12 bytes in 592 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:32:37 2025
p = 0.118
------
mix3 extreme = 1.53717 (sig = 01000000) weight 1 (16), p-value = 0.88
mix3 extreme = 3.32377 (sig = 10100000) weight 2 (112), p-value = 0.0947
mix3 extreme = 3.12416 (sig = 02200001) weight 3 (448), p-value = 0.55
mix3 extreme = 3.46863 (sig = 02110100) weight 4 (1120), p-value = 0.443
mix3 extreme = 4.84658 (sig = 20221222) weight >=5 (4864), p-value = 0.00609
bits per word = 64 (analyzing bits); min category p-value = 0.00609
processed 2.5e+12 bytes in 740 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:35:05 2025
p = 0.0301
------
mix3 extreme = 1.61173 (sig = 00000200) weight 1 (16), p-value = 0.837
mix3 extreme = 3.37008 (sig = 10100000) weight 2 (112), p-value = 0.0807
mix3 extreme = 3.93348 (sig = 00202100) weight 3 (448), p-value = 0.0368
mix3 extreme = 3.29910 (sig = 02200021) weight 4 (1120), p-value = 0.663
mix3 extreme = 4.29826 (sig = 20221222) weight >=5 (4864), p-value = 0.0803
bits per word = 64 (analyzing bits); min category p-value = 0.0368
processed 3e+12 bytes in 887 seconds (3.382 GB/s, 12.18 TB/h). Fri Feb 21 23:37:32 2025
p = 0.171
------
mix3 extreme = 2.09163 (sig = 00000200) weight 1 (16), p-value = 0.448
mix3 extreme = 2.61382 (sig = 01100000) weight 2 (112), p-value = 0.635
mix3 extreme = 3.98155 (sig = 00202100) weight 3 (448), p-value = 0.0302
mix3 extreme = 3.38573 (sig = 00002211) weight 4 (1120), p-value = 0.549
mix3 extreme = 4.15291 (sig = 20211110) weight >=5 (4864), p-value = 0.148
bits per word = 64 (analyzing bits); min category p-value = 0.0302
processed 4e+12 bytes in 1.18e+03 seconds (3.381 GB/s, 12.17 TB/h). Fri Feb 21 23:42:28 2025
p = 0.142
------
```
------------------------------------------------------------
------------------------------------------------------------
# `./fio-stl/000 copyright.h`
1 public symbols.
### Macros
#### `FIO_INCLUDE_FILE`
```c
#define FIO_INCLUDE_FILE "fio-stl.h"
```
Persists the include file name (single file vs. folder)
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/000 core.h`
1764 public symbols.
### Definition / Code Generation Macros
#### `FIO_DEF_GET_FUNC_DEC`
```c
#define FIO_DEF_GET_FUNC_DEC(static, namespace, T_type, F_type, field_name) \
/** Returns current value of property within the struct / union. */ \
static F_type FIO_NAME(namespace, field_name)(T_type * o);
```
Declares (names) a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GET_FUNC`
```c
#define FIO_DEF_GET_FUNC(static, namespace, T_type, F_type, field_name) \
/** Returns current value of property within the struct / union. */ \
static F_type FIO_NAME(namespace, field_name)(T_type * o) { \
FIO_ASSERT_DEBUG(o, "NULL " FIO_MACRO2STR(namespace) " pointer @ `get`!"); \
return o->field_name; \
}
```
Defines a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_SET_FUNC_DEC`
```c
#define FIO_DEF_SET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
/** Sets a new value, returning the old one */ \
static F_type FIO_NAME(FIO_NAME(namespace, F_name), set)(T_type * o, \
F_type new_value);
```
Declares (names) a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_SET_FUNC`
```c
#define FIO_DEF_SET_FUNC(static, namespace, T_type, F_type, F_name, on_set) \
/** Sets a new value, returning the old one */ \
static F_type FIO_NAME(FIO_NAME(namespace, F_name), set)(T_type * o, \
F_type new_value) { \
FIO_ASSERT_DEBUG(o, "NULL " FIO_MACRO2STR(namespace) " pointer @ `set`!"); \
F_type old_value = o->F_name; \
o->F_name = new_value; \
on_set(o); \
return old_value; \
}
```
Defines a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GETSET_FUNC`
```c
#define FIO_DEF_GETSET_FUNC(static, namespace, T_type, F_type, F_name, on_set) \
FIO_DEF_GET_FUNC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_SET_FUNC(static, namespace, T_type, F_type, F_name, on_set)
```
Defines `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GETSET_FUNC_DEC`
```c
#define FIO_DEF_GETSET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_GET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_SET_FUNC_DEC(static, namespace, T_type, F_type, F_name)
```
Declares (names) `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_GET`
```c
#define FIO_IFUNC_DEF_GET(namespace, T_type, F_type, field_name) \
FIO_DEF_GET_FUNC(FIO_IFUNC, namespace, T_type, F_type, field_name)
```
Defines a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_SET`
```c
#define FIO_IFUNC_DEF_SET(namespace, T_type, F_type, F_name, on_set) \
FIO_DEF_SET_FUNC(FIO_IFUNC, namespace, T_type, F_type, F_name, on_set)
```
Defines a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_GETSET`
```c
#define FIO_IFUNC_DEF_GETSET(namespace, T_type, F_type, F_name, on_set) \
FIO_IFUNC_DEF_GET(namespace, T_type, F_type, F_name) \
FIO_IFUNC_DEF_SET(namespace, T_type, F_type, F_name, on_set)
```
Defines `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_STATIC_ALLOC_DEF`
```c
#define FIO_STATIC_ALLOC_DEF(name, \
type_T, \
units_per_allocation, \
max_concurrent_allocations) \
/** Returns a slot from the `name` static arena. */ \
FIO_SFUNC FIO_WARN_UNUSED type_T *name(size_t count) { \
static type_T name##buffer[sizeof(type_T) * (max_concurrent_allocations) * \
(units_per_allocation)]; \
static size_t pos; \
if (!count) \
return name##buffer; \
size_t at = fio_atomic_add(&pos, count); \
at %= (max_concurrent_allocations); \
return (at * (units_per_allocation)) + name##buffer; \
} \
/** Returns the logical arena capacity in `type_T` units. */ \
FIO_IFUNC size_t name##_size(void) { \
return (size_t)((max_concurrent_allocations) * (units_per_allocation)); \
}
```
Defines a statically backed, round-robin allocator named `name`.
This defines the following functions:
```c
static type_T *name(size_t count);
static inline size_t name##_size(void);
```
The allocator has `max_concurrent_allocations` slots, each containing
`units_per_allocation` `type_T` elements. `name(count)` returns a pointer to
the slot selected by the atomic round-robin position. `count` advances that
position by the requested number of slots; it does not change the size of
the returned slot. Passing `0` returns the first slot without advancing.
The memory is static, correctly aligned for `type_T`, and must not be freed.
`name##_size()` returns the logical arena capacity in `type_T` units:
```c
max_concurrent_allocations * units_per_allocation
```
A returned slot remains valid until the allocator wraps and reuses it. Set
`max_concurrent_allocations` for the maximum number of outstanding slots,
including concurrent callers.
Example use:
```c
// defines a static allocator for 19-character strings
FIO_STATIC_ALLOC_DEF(numer2hex_allocator,
char,
19,
FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX);
// a function that returns an unsigned number as a 16 digit hex string
char *ntos16(uint16_t n) {
char *buf = numer2hex_allocator(1);
buf[0] = '0'; buf[1] = 'x';
fio_ltoa16u(buf + 2, n, 16);
buf[18] = 0;
return buf;
}
```
A similar approach is used by `fiobj_num2cstr` for temporary FIOBJ-to-string
conversions that do not require memory management.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_STATIC_SAFE_ALLOC_DEF`
```c
#define FIO_STATIC_SAFE_ALLOC_DEF(name, \
type_T, \
units_per_allocation, \
max_concurrent_allocations) \
enum { name##__hdr_ = (int)((sizeof(type_T) < 16) ? sizeof(type_T) : 16) }; \
typedef struct FIO_ALIGN(16) name##__slot_s { \
unsigned char busy_[name##__hdr_]; \
type_T data_[(units_per_allocation)]; \
} name##__slot_s; \
FIO_ASSERT_STATIC(offsetof(name##__slot_s, data_) == (size_t)name##__hdr_, \
"FIO_STATIC_SAFE_ALLOC_DEF: slot metadata header must be" \
" min(sizeof(type_T), 16) bytes"); \
/** Checks out a slot; returns its data block, or NULL when all are busy. */ \
FIO_SFUNC FIO_WARN_UNUSED type_T *name##_try(void) { \
static name##__slot_s name##buffer[(max_concurrent_allocations)]; \
static size_t hint; \
size_t start = fio_atomic_add(&hint, 1); \
for (size_t i = 0; i < (size_t)(max_concurrent_allocations); ++i) { \
size_t at = (start + i) % (size_t)(max_concurrent_allocations); \
if (!(fio_atomic_or(name##buffer[at].busy_, 1) & 1)) \
return name##buffer[at].data_; \
} \
return NULL; \
} \
/** Releases a slot previously returned by name##_try. */ \
FIO_SFUNC void name##_free(type_T *ptr) { \
name##__slot_s *slot = \
(name##__slot_s *)(void *)((char *)ptr - name##__hdr_); \
fio_atomic_and(slot->busy_, 0); \
} \
/** Returns the logical arena capacity in `type_T` units. */ \
FIO_IFUNC size_t name##_size(void) { \
return (size_t)((max_concurrent_allocations) * (units_per_allocation)); \
}
```
FIO_STATIC_SAFE_ALLOC_DEF(name, type_T, units_per_allocation,
max_concurrent_allocations)
A contention-safe variant of FIO_STATIC_ALLOC_DEF for short-lived scratch
slots with an explicit checkout lifecycle. Unlike the round-robin variant
(which silently reuses slots past `max_concurrent_allocations`), this
allocator returns NULL when every slot is busy, letting callers fall back
gracefully instead of corrupting an in-flight user of the slot.
Each slot carries a small metadata header holding the busy byte. The
header is `min(sizeof(type_T), 16)` bytes: one element for small types
(data naturally aligned for `type_T`), capped at 16 bytes for larger
types (data 16-byte aligned). `type_T` alignment must be <= 16 (enforced
at compile time).
Generated API:
type_T *name##_try(void); - checkout a slot, or NULL if all busy.
void name##_free(type_T *); - release a slot returned by name##_try.
size_t name##_size(void); - logical arena capacity in type_T units.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X64_DEF`
```c
#define FIO_UXXX_X64_DEF(name, bits) \
uint64x2_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 64bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X32_DEF`
```c
#define FIO_UXXX_X32_DEF(name, bits) \
uint32x4_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 32bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X16_DEF`
```c
#define FIO_UXXX_X16_DEF(name, bits) \
uint16x8_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 16bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X8_DEF`
```c
#define FIO_UXXX_X8_DEF(name, bits) uint8x16_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 8bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
### Macros
#### `FIO_NOOP`
```c
#define FIO_NOOP
```
An empty macro, adding white space. Used to avoid function like macros.
_Symbol type:_ `macro`
#### `FIO_NOOP_FN`
```c
#define FIO_NOOP_FN(...)
```
A no-op macro that takes arguments. i.e., for use in `FIO_DEF_SET_FUNC`
_Symbol type:_ `macro`
#### `FIO_NOOP_FN_NAME`
```c
#define FIO_NOOP_FN_NAME (void)
```
Macro for a No-Op function name (void).
_Symbol type:_ `macro`
#### `FIO_VERSION_MAJOR`
```c
#define FIO_VERSION_MAJOR 0
```
MAJOR version: API/ABI breaking changes.
_Symbol type:_ `macro`
#### `FIO_VERSION_MINOR`
```c
#define FIO_VERSION_MINOR 8
```
MINOR version: Deprecation, or significant features added. May break ABI.
_Symbol type:_ `macro`
#### `FIO_VERSION_PATCH`
```c
#define FIO_VERSION_PATCH 0
```
PATCH version: Bug fixes, minor features may be added.
_Symbol type:_ `macro`
#### `FIO_VERSION_STRING`
```c
#define FIO_VERSION_STRING \
FIO_MACRO2STR(FIO_VERSION_MAJOR) \
"." FIO_MACRO2STR(FIO_VERSION_MINOR) "." FIO_MACRO2STR( \
FIO_VERSION_PATCH) "-" FIO_VERSION_BUILD
```
Version as a String literal (MACRO).
_Symbol type:_ `macro`
#### `FIO_VERSION_BUILD`
```c
#define FIO_VERSION_BUILD ""
```
_Symbol type:_ `macro`
#### `FIO_LEAK_COUNTER`
```c
#define FIO_LEAK_COUNTER 1
```
Enables memory leak detection. Disable by setting this to zero.
_Symbol type:_ `macro`
#### `FIO_USE_THREAD_MUTEX`
```c
#define FIO_USE_THREAD_MUTEX 0
```
Selects between facio.io's spinlocks (false) and OS mutexes (true)
_Symbol type:_ `macro`
#### `FIO_UNALIGNED_ACCESS`
```c
#define FIO_UNALIGNED_ACCESS 1
```
Allows facil.io to attempt unaligned memory access on *some* CPU systems.
_Symbol type:_ `macro`
#### `FIO_LIMIT_INTRINSIC_BUFFER`
```c
#define FIO_LIMIT_INTRINSIC_BUFFER 1
```
Limits register consumption on some pseudo-intrinsics, using more loops
_Symbol type:_ `macro`
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT 1
```
Memory allocations should be secure by default (facil.io allocators only)
_Symbol type:_ `macro`
#### `FIO_MEM_PAGE_SIZE_LOG`
```c
#define FIO_MEM_PAGE_SIZE_LOG 12 /* assumes 4096 bytes per page */
```
Compile-time memory page size log (12 == 4096 bytes).
_Symbol type:_ `macro`
#### `FIO_MAP_WARNING_BITSIZE`
```c
#define FIO_MAP_WARNING_BITSIZE ((size_t)24)
```
iMap and Map allocation size warning (log2), 24 == 16Mb.
_Symbol type:_ `macro`
#### `FIO_ALIGN`
```c
#define FIO_ALIGN(bytes) __attribute__((aligned(bytes)))
```
Sets alignment is supported by compiler.
_Symbol type:_ `macro`
#### `FIO_COMPILER_GUARD`
```c
#define FIO_COMPILER_GUARD __asm__ volatile("" ::: "memory")
```
Clobber CPU registers and prevent compiler reordering optimizations.
_Symbol type:_ `macro`
#### `FIO_COMPILER_GUARD_INSTRUCTION`
```c
#define FIO_COMPILER_GUARD_INSTRUCTION __asm__ volatile("" :::)
```
Prevent compiler reordering optimizations.
_Symbol type:_ `macro`
#### `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED`
```c
#define FIO_UNALIGNED_MEMORY_ACCESS_ENABLED 1
```
True when unaligned memory is allowed.
_Symbol type:_ `macro`
#### `FIO_OS_POSIX`
```c
#define FIO_OS_POSIX 0
```
Always defined, true on POSIX systems.
_Symbol type:_ `macro`
#### `FIO_OS_WIN`
```c
#define FIO_OS_WIN 0
```
Always defined, true on Windows systems.
_Symbol type:_ `macro`
#### `FIO_KILL_SELF`
```c
#define FIO_KILL_SELF() GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)
```
Sends a signal to kill the current process (emulates CTRL + C).
_Symbol type:_ `macro`
#### `FIO_HAVE_UNIX_TOOLS`
```c
#define FIO_HAVE_UNIX_TOOLS 2
```
True if POSIX headers are available.
_Symbol type:_ `macro`
#### `FIO_SIZE_T_ERROR`
```c
#define FIO_SIZE_T_ERROR (~(size_t)0)
```
The `-1` value for a `size_t` type, often used as an error value.
_Symbol type:_ `macro`
#### `FIO_MAYBE_UNUSED`
```c
#define FIO_MAYBE_UNUSED __attribute__((unused))
```
Marks a variable / function as possibly unused (if compiler supported).
_Symbol type:_ `macro`
#### `FIO_SFUNC`
```c
#define FIO_SFUNC static FIO_MAYBE_UNUSED
```
static, possibly unused, function.
_Symbol type:_ `macro`
#### `FIO_IFUNC`
```c
#define FIO_IFUNC FIO_SFUNC inline
```
Marks a function as `static`, `inline` and possibly unused.
_Symbol type:_ `macro`
#### `FIO_WARN_UNUSED`
```c
#define FIO_WARN_UNUSED __attribute__((warn_unused_result))
```
Attribute for functions whose return value should not be ignored.
_Symbol type:_ `macro`
#### `FIO_MIFN`
```c
#define FIO_MIFN FIO_IFUNC FIO_WARN_UNUSED
```
Marks a function as inline with warn_unused_result (for math functions).
_Symbol type:_ `macro`
#### `FIO_CONST`
```c
#define FIO_CONST __attribute__((const))
```
Marks a function as const (no side effects, result depends only on args)
_Symbol type:_ `macro`
#### `FIO_PURE`
```c
#define FIO_PURE __attribute__((pure))
```
Marks a function as pure (no side effects, may read global memory)
_Symbol type:_ `macro`
#### `FIO_WEAK`
```c
#define FIO_WEAK __attribute__((weak))
```
Marks a function as weak
_Symbol type:_ `macro`
#### `FIO_WEAK_VAR`
```c
#define FIO_WEAK_VAR __declspec(selectany)
```
Marks a global variable as weak
_Symbol type:_ `macro`
#### `FIO_CONSTRUCTOR`
```c
#define FIO_CONSTRUCTOR(fname) \
static __attribute__((constructor)) void fname(void)
```
Marks a function as a constructor - if supported.
_Symbol type:_ `macro`
#### `FIO_DESTRUCTOR`
```c
#define FIO_DESTRUCTOR(fname) \
static __attribute__((destructor)) void fname(void)
```
Marks a function as a destructor - if supported. Consider using atexit()
_Symbol type:_ `macro`
#### `FIO_LIKELY`
```c
#define FIO_LIKELY(cond) __builtin_expect((cond), 1)
```
Provides a compiler hint for value likelihood, if possible.
_Symbol type:_ `macro`
#### `FIO_UNLIKELY`
```c
#define FIO_UNLIKELY(cond) __builtin_expect((cond), 0)
```
Provides a compiler hint for value likelihood, if possible.
_Symbol type:_ `macro`
#### `FIO_PREFETCH`
```c
#define FIO_PREFETCH(ptr) __builtin_prefetch((ptr), 0, 3)
```
Prefetch data for reading into all cache levels.
_Symbol type:_ `macro`
#### `FIO_PREFETCH_W`
```c
#define FIO_PREFETCH_W(ptr) __builtin_prefetch((ptr), 1, 3)
```
Prefetch data for writing (exclusive cache line ownership).
_Symbol type:_ `macro`
#### `FIO_PREFETCH_NT`
```c
#define FIO_PREFETCH_NT(ptr) __builtin_prefetch((ptr), 0, 0)
```
Prefetch non-temporal (streaming data, minimize cache pollution).
_Symbol type:_ `macro`
#### `FIO_MACRO2STR`
```c
#define FIO_MACRO2STR(macro) FIO___MACRO2STR_STEP2(macro)
```
Converts a macro's content to a string literal.
_Symbol type:_ `macro`
#### `FIO_NAME`
```c
#define FIO_NAME(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, _)
```
Used for naming functions and variables resulting in: prefix_postfix
_Symbol type:_ `macro`
#### `FIO_NAME2`
```c
#define FIO_NAME2(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, 2)
```
Sets naming convention for conversion functions, i.e.: foo2bar
_Symbol type:_ `macro`
#### `FIO_NAME_BL`
```c
#define FIO_NAME_BL(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, _is_)
```
Sets naming convention for boolean testing functions, i.e.: foo_is_true
_Symbol type:_ `macro`
#### `FIO_NAME_TEST`
```c
#define FIO_NAME_TEST(prefix, postfix) \
FIO_NAME(fio___test, FIO_NAME(prefix, postfix))
```
Used internally to name test functions.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_LMASK`
```c
#define FIO_PTR_MATH_LMASK(T_type, ptr, bits) \
((T_type *)(((uintptr_t)(ptr)) & (((uintptr_t)1ULL << (bits)) - 1)))
```
Masks a pointer's left-most bits, returning the right bits.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_RMASK`
```c
#define FIO_PTR_MATH_RMASK(T_type, ptr, bits) \
((T_type *)(((uintptr_t)(ptr)) & ((~(uintptr_t)0ULL) << (bits))))
```
Masks a pointer's right-most bits, returning the left bits.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_ADD`
```c
#define FIO_PTR_MATH_ADD(T_type, ptr, offset) \
((T_type *)((uintptr_t)(ptr) + (uintptr_t)(offset)))
```
Add offset bytes to pointer, updating the pointer's type.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_SUB`
```c
#define FIO_PTR_MATH_SUB(T_type, ptr, offset) \
((T_type *)((uintptr_t)(ptr) - (uintptr_t)(offset)))
```
Subtract X bytes from pointer, updating the pointer's type.
_Symbol type:_ `macro`
#### `FIO_PTR_FIELD_OFFSET`
```c
#define FIO_PTR_FIELD_OFFSET(T_type, field) \
((uintptr_t)((&((T_type *)0xFF00)->field)) - 0xFF00)
```
Find the root object (of a struct) from it's field (with sanitizer fix).
_Symbol type:_ `macro`
#### `FIO_PTR_FROM_FIELD`
```c
#define FIO_PTR_FROM_FIELD(T_type, field, ptr) \
FIO_PTR_MATH_SUB(T_type, ptr, FIO_PTR_FIELD_OFFSET(T_type, field))
```
Find the root object (of a struct) from it's field (with sanitizer fix).
_Symbol type:_ `macro`
#### `FIO_THREAD_WAIT`
```c
#define FIO_THREAD_WAIT(nano_sec) \
do { \
const struct timespec tm = {.tv_sec = (time_t)((nano_sec) / 1000000000), \
.tv_nsec = ((long)(nano_sec) % 1000000000)}; \
nanosleep(&tm, (struct timespec *)NULL); \
} while (0)
```
Calls nanonsleep with the requested nano-second count.
_Symbol type:_ `macro`
#### `FIO_THREAD_YIELD`
```c
#define FIO_THREAD_YIELD() __asm__ __volatile__("pause" ::: "memory")
```
Yields the thread, hinting to the processor about spinlock loop.
_Symbol type:_ `macro`
#### `FIO_THREAD_RESCHEDULE`
```c
#define FIO_THREAD_RESCHEDULE() FIO_THREAD_WAIT(4)
```
Reschedules the thread by calling nanosleeps for nano-seconds.
In practice, the thread will probably sleep for 60ns or more.
Seems to be faster then thread_yield, perhaps it prevents de-prioritization
of the thread.
_Symbol type:_ `macro`
#### `FIO_LOCK_INIT`
```c
#define FIO_LOCK_INIT 0
```
Initialization value for a spin lock.
_Symbol type:_ `macro`
#### `FIO_LOCK_SUBLOCK`
```c
#define FIO_LOCK_SUBLOCK(sub) ((uint8_t)(1U) << ((sub)&7))
```
A numbered sub-lock index, must be less than 8. Zero == the master lock.
_Symbol type:_ `macro`
#### `FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`
```c
#define FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX 256
```
Default slot count used by selected internal static allocators.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_NONE`
```c
#define FIO_LOG_LEVEL_NONE 0
```
Logging level of zero (no logging).
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_FATAL`
```c
#define FIO_LOG_LEVEL_FATAL 1
```
Log fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_ERROR`
```c
#define FIO_LOG_LEVEL_ERROR 2
```
Log errors and fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_WARNING`
```c
#define FIO_LOG_LEVEL_WARNING 3
```
Log warnings, errors and fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_INFO`
```c
#define FIO_LOG_LEVEL_INFO 4
```
Log every message (info, warnings, errors and fatal errors).
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_DEBUG`
```c
#define FIO_LOG_LEVEL_DEBUG 5
```
Log everything, including debug messages.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_SET`
```c
#define FIO_LOG_LEVEL_SET(new_level) (0)
```
Sets the Logging Level
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_GET`
```c
#define FIO_LOG_LEVEL_GET() (0)
```
Returns the Logging Level
_Symbol type:_ `macro`
#### `FIO_LOG_WRITE`
```c
#define FIO_LOG_WRITE(...) FIO_LOG2STDERR("(" FIO___FILE__ ":" FIO_MACRO2STR(__LINE__) "): " __VA_ARGS__)
```
Writes a message to the log, including the file name and line number.
_Symbol type:_ `macro`
#### `FIO_LOG_FATAL`
```c
#define FIO_LOG_FATAL(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_FATAL, "\x1B[1m\x1B[7mFATAL:\x1B[0m " __VA_ARGS__)
```
Writes a Fatal message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_ERROR`
```c
#define FIO_LOG_ERROR(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_ERROR, "\x1B[1mERROR:\x1B[0m " __VA_ARGS__)
```
Writes a Error message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_SECURITY`
```c
#define FIO_LOG_SECURITY(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_ERROR, "\x1B[1mSECURITY:\x1B[0m " __VA_ARGS__)
```
Writes a Security message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_WARNING`
```c
#define FIO_LOG_WARNING(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_WARNING, "\x1B[2mWARNING:\x1B[0m " __VA_ARGS__)
```
Writes a Warning message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_INFO`
```c
#define FIO_LOG_INFO(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_INFO, "INFO: " __VA_ARGS__)
```
Writes a Info message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_DEBUG`
```c
#define FIO_LOG_DEBUG(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_DEBUG,"DEBUG: (" FIO___FILE__ ":" FIO_MACRO2STR(__LINE__) ") " __VA_ARGS__)
```
Writes a Debug message to the log, including the file name and line number.
_Symbol type:_ `macro`
#### `FIO_LOG_DEBUG2`
```c
#define FIO_LOG_DEBUG2(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_DEBUG, "DEBUG: " __VA_ARGS__)
```
Writes a Debug message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_DDEBUG`
```c
#define FIO_LOG_DDEBUG(...) FIO_LOG_DEBUG(__VA_ARGS__)
```
Writes a Debug message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DDEBUG2`
```c
#define FIO_LOG_DDEBUG2(...) FIO_LOG_DEBUG2(__VA_ARGS__)
```
Writes a Debug2 message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DERROR`
```c
#define FIO_LOG_DERROR(...) FIO_LOG_ERROR(__VA_ARGS__)
```
Writes a Error message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DSECURITY`
```c
#define FIO_LOG_DSECURITY(...) FIO_LOG_SECURITY(__VA_ARGS__)
```
Writes a Security message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DWARNING`
```c
#define FIO_LOG_DWARNING(...) FIO_LOG_WARNING(__VA_ARGS__)
```
Writes a Warning message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DINFO`
```c
#define FIO_LOG_DINFO(...) FIO_LOG_INFO(__VA_ARGS__)
```
Writes a Info message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_LENGTH_LIMIT`
```c
#define FIO_LOG_LENGTH_LIMIT 1024
```
Defines a point at which logging truncates (limits stack memory use)
_Symbol type:_ `macro`
#### `FIO_LOG2STDERR`
```c
#define FIO_LOG2STDERR(...)
```
Prints to STDERR, attempting to use only stack allocated memory.
_Symbol type:_ `macro`
#### `FIO_STDERR_FILE`
```c
#define FIO_STDERR_FILE stderr
```
_Symbol type:_ `macro`
#### `FIO_ASSERT`
```c
#define FIO_ASSERT(cond, ...) \
do { \
if (FIO_UNLIKELY(!(cond))) { \
FIO_LOG_FATAL(__VA_ARGS__); \
FIO_LOG_FATAL(" errno(%d): %s\n", errno, strerror(errno)); \
fflush(FIO_STDERR_FILE); \
FIO___ASSERT_PERFORM_SIGNAL(); \
exit(-1); \
} \
} while (0)
```
Asserts a condition is true, or kills the application using SIGINT.
_Symbol type:_ `macro`
#### `FIO_ASSERT_ALLOC`
```c
#define FIO_ASSERT_ALLOC(ptr) FIO_ASSERT((ptr), "memory allocation failed.")
```
Tests for an allocation failure. The behavior can be overridden.
_Symbol type:_ `macro`
#### `FIO_ASSERT_DEBUG`
```c
#define FIO_ASSERT_DEBUG(cond, ...) \
do { \
if (!(cond)) { \
FIO_LOG_FATAL("(" FIO___FILE__ \
":" FIO_MACRO2STR(__LINE__) ") " __VA_ARGS__); \
FIO_LOG_FATAL(" errno(%d): %s\n", errno, strerror(errno)); \
fflush(FIO_STDERR_FILE); \
FIO___ASSERT_PERFORM_SIGNAL(); \
exit(-1); \
} \
} while (0)
```
If `DEBUG` is defined, raises SIGINT if assertion fails, otherwise NOOP.
_Symbol type:_ `macro`
#### `FIO_ASSERT_STATIC`
```c
#define FIO_ASSERT_STATIC(cond, msg) _Static_assert((cond), msg)
```
Perform a static (build time) assertion.
_Symbol type:_ `macro`
#### `FIO_MEM_STACK_WIPE`
```c
#define FIO_MEM_STACK_WIPE(pages) \
do { \
volatile char stack_mem[(pages) << 12] = {0}; \
(void)stack_mem; \
} while (0)
```
Places `pages` x 4096 bytes of zero (`0`) on the stack.
_Symbol type:_ `macro`
#### `FIO_MEMCPY`
```c
#define FIO_MEMCPY fio_memcpy
```
Use this macro instead of `memcpy`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMMOVE`
```c
#define FIO_MEMMOVE fio_memcpy
```
Use this macro instead of `memmove`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMCMP`
```c
#define FIO_MEMCMP fio_memcmp
```
Use this macro instead of `memcmp`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMCHR`
```c
#define FIO_MEMCHR fio_memchr
```
Use this macro instead of `memchr`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMSET`
```c
#define FIO_MEMSET fio_memset
```
Use this macro instead of `memset`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_STRLEN`
```c
#define FIO_STRLEN fio_strlen
```
Use this macro instead of `strlen`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_FOR`
```c
#define FIO_FOR(i, count) for (size_t i = 0; i < (count); ++i)
```
Helper for simple `for` loops, where `i` is the variable name to use.
_Symbol type:_ `macro`
#### `FIO_FOR_UNROLL_GROUP_SIZE`
```c
#define FIO_FOR_UNROLL_GROUP_SIZE ((size_t)256U)
```
The byte stride used by `FIO_FOR_UNROLL` for compiler-friendly batches.
Most CPUs should be able to hold at least 512 bytes in their registers. The
default value assumes half that is available for the optimized loop, as the
action within each loop will likely require some available registers.
_Symbol type:_ `macro`
#### `FIO_FOR_UNROLL`
```c
#define FIO_FOR_UNROLL(iterations, size_of_loop, i, action) \
do { \
size_t i = 0; \
const size_t fio___unroll_remainder__ = \
((iterations) & ((FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)) - 1)); \
/* handle odd length vectors, not multiples of FIO___LOG2V */ \
if (fio___unroll_remainder__ && ((iterations) + 1)) \
for (; i < fio___unroll_remainder__; ++i) \
action; \
if (iterations) \
for (; !((iterations) + 1) || (i < (iterations));) { \
for (size_t j__loop__ = 0; \
j__loop__ < (FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)); \
++j__loop__, ++i) /* dear compiler, please vectorize */ \
action; \
} \
} while (0)
```
Unrolled `for` loop - separates for loops to make it easier for the compiler
to optimize.
**Parameters:**
- `iterations` - the number of loop iterations to perform
- `size_of_loop` - the number of bytes consumed by each `action`
- `i` - the loop index variable name to use (accessible by `action`)
- `action` - an action to be performed each iteration (can be a macro)
Note that `action` is NOT guarded. In the following `strlen` example, the
`if` statement will run only after each SIMD loop has ended, allowing the
compiler to unroll the loops:
```c
size_t has_zero = 0;
const size_t iterations = ((~(uintptr_t)str) + 1) | (1ULL << 31);
#define FIO___STRLEN_ACTION
has_zero += (!str[i] | !!has_zero);
if (has_zero)
return i - has_zero;
FIO_FOR_UNROLL(iterations, 1, i, FIO___STRLEN_ACTION);
#undef FIO___STRLEN_ACTION
return ~(size_t)0;
```
_Symbol type:_ `macro`
#### `FIO_LEAK_COUNTER_SKIP_EXIT`
```c
#define FIO_LEAK_COUNTER_SKIP_EXIT 0
```
If set, no `FIO_CALL_AFTER_EXIT` leak reporting event is registered.
_Symbol type:_ `macro`
#### `FIO_SHIFT_FORWARDS`
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) >> (bits))
```
An endianess dependent shift operation, moves bits forwards.
_Symbol type:_ `macro`
#### `FIO_SHIFT_BACKWARDS`
```c
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) << (bits))
```
An endianess dependent shift operation, moves bits backwards.
_Symbol type:_ `macro`
#### `FIO_LIST_NODE`
```c
#define FIO_LIST_NODE fio_list_node_s
```
A linked list node type
_Symbol type:_ `macro`
#### `FIO_LIST_HEAD`
```c
#define FIO_LIST_HEAD fio_list_node_s
```
A linked list head type
_Symbol type:_ `macro`
#### `FIO_LIST_INIT`
```c
#define FIO_LIST_INIT(obj) \
(fio_list_node_s) { .next = &(obj), .prev = &(obj) }
```
Allows initialization of FIO_LIST_HEAD objects.
_Symbol type:_ `macro`
#### `FIO_LIST_EACH`
```c
#define FIO_LIST_EACH(type, node_name, head, pos) \
for (type *pos = \
((head)->next ? FIO_PTR_FROM_FIELD(type, node_name, (head)->next) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))), \
*next____p_ls_##pos = \
((head)->next \
? FIO_PTR_FROM_FIELD(type, node_name, (head)->next->next) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.next)))
```
Loops through every node in the linked list except the head.
_Symbol type:_ `macro`
#### `FIO_LIST_EACH_REVERSED`
```c
#define FIO_LIST_EACH_REVERSED(type, node_name, head, pos) \
for (type *pos = \
((head)->prev ? FIO_PTR_FROM_FIELD(type, node_name, (head)->prev) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))), \
*next____p_ls_##pos = \
((head)->prev \
? FIO_PTR_FROM_FIELD(type, node_name, (head)->prev->prev) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.prev)))
```
Loops through every node in the linked list except the head.
_Symbol type:_ `macro`
#### `FIO_LIST_PUSH`
```c
#define FIO_LIST_PUSH(head, n) \
do { \
(n)->prev = (head)->prev; \
(n)->next = (head); \
(head)->prev->next = (n); \
(head)->prev = (n); \
} while (0)
```
UNSAFE macro for pushing a node to a list.
_Symbol type:_ `macro`
#### `FIO_LIST_REMOVE`
```c
#define FIO_LIST_REMOVE(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
} while (0)
```
UNSAFE macro for removing a node from a list.
_Symbol type:_ `macro`
#### `FIO_LIST_REMOVE_RESET`
```c
#define FIO_LIST_REMOVE_RESET(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
(n)->next = (n)->prev = (n); \
} while (0)
```
UNSAFE macro for removing a node from a list. Resets node data.
_Symbol type:_ `macro`
#### `FIO_LIST_POP`
```c
#define FIO_LIST_POP(type, node_name, dest_ptr, head) \
do { \
(dest_ptr) = FIO_PTR_FROM_FIELD(type, node_name, ((head)->next)); \
FIO_LIST_REMOVE(&(dest_ptr)->node_name); \
} while (0)
```
UNSAFE macro for popping a node to a list.
_Symbol type:_ `macro`
#### `FIO_LIST_IS_EMPTY`
```c
#define FIO_LIST_IS_EMPTY(head) \
((!(head)) || ((!(head)->next) | ((head)->next == (head))))
```
UNSAFE macro for testing if a list is empty.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST32_NODE`
```c
#define FIO_INDEXED_LIST32_NODE fio_index32_node_s
```
A 32 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST32_HEAD`
```c
#define FIO_INDEXED_LIST32_HEAD uint32_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST16_NODE`
```c
#define FIO_INDEXED_LIST16_NODE fio_index16_node_s
```
A 16 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST16_HEAD`
```c
#define FIO_INDEXED_LIST16_HEAD uint16_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST8_NODE`
```c
#define FIO_INDEXED_LIST8_NODE fio_index8_node_s
```
An 8 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST8_HEAD`
```c
#define FIO_INDEXED_LIST8_HEAD uint8_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_PUSH`
```c
#define FIO_INDEXED_LIST_PUSH(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.prev = (root)[(head)].node_name.prev; \
(root)[n__].node_name.next = (head); \
(root)[(root)[(head)].node_name.prev].node_name.next = (n__); \
(root)[(head)].node_name.prev = (n__); \
} while (0)
```
UNSAFE macro for pushing a node to a list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_UNSHIFT`
```c
#define FIO_INDEXED_LIST_UNSHIFT(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.next = (root)[(head)].node_name.next; \
(root)[n__].node_name.prev = (head); \
(root)[(root)[(head)].node_name.next].node_name.prev = (n__); \
(root)[(head)].node_name.next = (n__); \
(head) = (n__); \
} while (0)
```
UNSAFE macro for adding a node to the begging of the list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_REMOVE`
```c
#define FIO_INDEXED_LIST_REMOVE(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
} while (0)
```
UNSAFE macro for removing a node from a list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_REMOVE_RESET`
```c
#define FIO_INDEXED_LIST_REMOVE_RESET(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
(root)[n__].node_name.next = (root)[n__].node_name.prev = (n__); \
} while (0)
```
UNSAFE macro for removing a node from a list. Resets node data.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_EACH`
```c
#define FIO_INDEXED_LIST_EACH(root, node_name, head, pos) \
for (size_t pos = (head), \
stooper___hd = (head), \
stopper___ils___ = 0, \
pos##___nxt = (root)[(head)].node_name.next; \
!stopper___ils___; \
(stopper___ils___ = ((pos = pos##___nxt) == stooper___hd)), \
pos##___nxt = (root)[pos].node_name.next)
```
Loops through every index in the indexed list, assuming `head` is valid.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_EACH_REVERSED`
```c
#define FIO_INDEXED_LIST_EACH_REVERSED(root, node_name, head, pos) \
for (size_t pos = ((root)[(head)].node_name.prev), \
pos##___nxt = \
((root)[((root)[(head)].node_name.prev)].node_name.prev), \
stooper___hd = (head), \
stopper___ils___ = 0; \
!stopper___ils___; \
((stopper___ils___ = (pos == stooper___hd)), \
(pos = pos##___nxt), \
(pos##___nxt = (root)[pos##___nxt].node_name.prev)))
```
Loops through every index in the indexed list, assuming `head` is valid.
_Symbol type:_ `macro`
#### `FIO_LROT`
```c
#define FIO_LROT(i, bits) \
(((i) << ((bits) & ((sizeof((i)) << 3) - 1))) | \
((i) >> (((uint8_t)(-(bits))) & ((sizeof((i)) << 3) - 1))))
```
Left rotation for an unknown size element, inlined.
_Symbol type:_ `macro`
#### `FIO_RROT`
```c
#define FIO_RROT(i, bits) \
(((i) >> ((bits) & ((sizeof((i)) << 3) - 1))) | \
((i) << (((uint8_t)(-(bits))) & ((sizeof((i)) << 3) - 1))))
```
Right rotation for an unknown size element, inlined.
_Symbol type:_ `macro`
#### `FIO_HAS_ZERO_BYTE64`
```c
#define FIO_HAS_ZERO_BYTE64(row) \
(((row)-UINT64_C(0x0101010101010101)) & \
((~(row)) & UINT64_C(0x8080808080808080)))
```
_Symbol type:_ `macro`
#### `FIO_HAS_FULL_BYTE64`
```c
#define FIO_HAS_FULL_BYTE64(row) \
((((row)&UINT64_C(0x7F7F7F7F7F7F7F7F)) + UINT64_C(0x0101010101010101)) & \
(row)&UINT64_C(0x8080808080808080))
```
_Symbol type:_ `macro`
#### `FIO_HAS_BYTE2BITMAP`
```c
#define FIO_HAS_BYTE2BITMAP(result, bit_index) \
do { \
(result) = fio_ltole64((result)); /* map little endian to bitmap */ \
(result) >>= bit_index; /* move bit index to 0x01 */ \
(result) |= (result) >> 7; /* pack all 0x80 bits into one byte */ \
(result) |= (result) >> 14; \
(result) |= (result) >> 28; \
(result) &= 0xFFU; \
} while (0)
```
Converts a FIO_HAS_FULL_BYTE64 result to relative position bitmap.
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME0`
```c
#define FIO_U8_HASH_PRIME0 0x17U /* (4/6) 00010111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME1`
```c
#define FIO_U8_HASH_PRIME1 0x1DU /* (4/6) 00011101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME2`
```c
#define FIO_U8_HASH_PRIME2 0x25U /* (3/6) 00100101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME3`
```c
#define FIO_U8_HASH_PRIME3 0x29U /* (3/6) 00101001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME4`
```c
#define FIO_U8_HASH_PRIME4 0x2BU /* (4/6) 00101011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME5`
```c
#define FIO_U8_HASH_PRIME5 0x35U /* (4/6) 00110101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME6`
```c
#define FIO_U8_HASH_PRIME6 0x3BU /* (5/6) 00111011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME7`
```c
#define FIO_U8_HASH_PRIME7 0x43U /* (3/8) 01000011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME8`
```c
#define FIO_U8_HASH_PRIME8 0x47U /* (4/8) 01000111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME9`
```c
#define FIO_U8_HASH_PRIME9 0x49U /* (3/8) 01001001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME10`
```c
#define FIO_U8_HASH_PRIME10 0x53U /* (4/8) 01010011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME11`
```c
#define FIO_U8_HASH_PRIME11 0x59U /* (4/8) 01011001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME12`
```c
#define FIO_U8_HASH_PRIME12 0x61U /* (3/8) 01100001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME13`
```c
#define FIO_U8_HASH_PRIME13 0x65U /* (4/8) 01100101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME14`
```c
#define FIO_U8_HASH_PRIME14 0x67U /* (5/8) 01100111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME15`
```c
#define FIO_U8_HASH_PRIME15 0x6BU /* (5/8) 01101011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME16`
```c
#define FIO_U8_HASH_PRIME16 0x6DU /* (5/8) 01101101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME17`
```c
#define FIO_U8_HASH_PRIME17 0x83U /* (3/8) 10000011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME18`
```c
#define FIO_U8_HASH_PRIME18 0x89U /* (3/8) 10001001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME19`
```c
#define FIO_U8_HASH_PRIME19 0x8BU /* (4/8) 10001011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME20`
```c
#define FIO_U8_HASH_PRIME20 0x95U /* (4/8) 10010101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME21`
```c
#define FIO_U8_HASH_PRIME21 0x97U /* (5/8) 10010111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME22`
```c
#define FIO_U8_HASH_PRIME22 0x9DU /* (5/8) 10011101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME23`
```c
#define FIO_U8_HASH_PRIME23 0xA3U /* (4/8) 10100011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME24`
```c
#define FIO_U8_HASH_PRIME24 0xA7U /* (5/8) 10100111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME25`
```c
#define FIO_U8_HASH_PRIME25 0xADU /* (5/8) 10101101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME26`
```c
#define FIO_U8_HASH_PRIME26 0xB3U /* (5/8) 10110011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME27`
```c
#define FIO_U8_HASH_PRIME27 0xB5U /* (5/8) 10110101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME28`
```c
#define FIO_U8_HASH_PRIME28 0xC1U /* (3/8) 11000001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME29`
```c
#define FIO_U8_HASH_PRIME29 0xC5U /* (4/8) 11000101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME30`
```c
#define FIO_U8_HASH_PRIME30 0xC7U /* (5/8) 11000111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME31`
```c
#define FIO_U8_HASH_PRIME31 0xD3U /* (5/8) 11010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME0`
```c
#define FIO_U16_HASH_PRIME0 0x631DU /* 0110001100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME1`
```c
#define FIO_U16_HASH_PRIME1 0x4F19U /* 0100111100011001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME2`
```c
#define FIO_U16_HASH_PRIME2 0xA91BU /* 1010100100011011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME3`
```c
#define FIO_U16_HASH_PRIME3 0xDF01U /* 1101111100000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME4`
```c
#define FIO_U16_HASH_PRIME4 0x8C5DU /* 1000110001011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME5`
```c
#define FIO_U16_HASH_PRIME5 0xF941U /* 1111100101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME6`
```c
#define FIO_U16_HASH_PRIME6 0xC49DU /* 1100010010011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME7`
```c
#define FIO_U16_HASH_PRIME7 0xA32BU /* 1010001100101011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME8`
```c
#define FIO_U16_HASH_PRIME8 0x7859U /* 0111100001011001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME9`
```c
#define FIO_U16_HASH_PRIME9 0xC4F1U /* 1100010011110001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME10`
```c
#define FIO_U16_HASH_PRIME10 0x74E1U /* 0111010011100001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME11`
```c
#define FIO_U16_HASH_PRIME11 0xD433U /* 1101010000110011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME12`
```c
#define FIO_U16_HASH_PRIME12 0xCB29U /* 1100101100101001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME13`
```c
#define FIO_U16_HASH_PRIME13 0xC2A7U /* 1100001010100111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME14`
```c
#define FIO_U16_HASH_PRIME14 0xC317U /* 1100001100010111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME15`
```c
#define FIO_U16_HASH_PRIME15 0x92B9U /* 1001001010111001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME16`
```c
#define FIO_U16_HASH_PRIME16 0x7D03U /* 0111110100000011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME17`
```c
#define FIO_U16_HASH_PRIME17 0x5CD1U /* 0101110011010001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME18`
```c
#define FIO_U16_HASH_PRIME18 0x73C1U /* 0111001111000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME19`
```c
#define FIO_U16_HASH_PRIME19 0x69A3U /* 0110100110100011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME20`
```c
#define FIO_U16_HASH_PRIME20 0xA2B3U /* 1010001010110011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME21`
```c
#define FIO_U16_HASH_PRIME21 0x521FU /* 0101001000011111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME22`
```c
#define FIO_U16_HASH_PRIME22 0x4E53U /* 0100111001010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME23`
```c
#define FIO_U16_HASH_PRIME23 0xFC41U /* 1111110001000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME24`
```c
#define FIO_U16_HASH_PRIME24 0x5F09U /* 0101111100001001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME25`
```c
#define FIO_U16_HASH_PRIME25 0x605FU /* 0110000001011111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME26`
```c
#define FIO_U16_HASH_PRIME26 0xA715U /* 1010011100010101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME27`
```c
#define FIO_U16_HASH_PRIME27 0x6C65U /* 0110110001100101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME28`
```c
#define FIO_U16_HASH_PRIME28 0x65C5U /* 0110010111000101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME29`
```c
#define FIO_U16_HASH_PRIME29 0x85D3U /* 1000010111010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME30`
```c
#define FIO_U16_HASH_PRIME30 0xDE41U /* 1101111001000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME31`
```c
#define FIO_U16_HASH_PRIME31 0xCA8DU /* 1100101010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME0`
```c
#define FIO_U32_HASH_PRIME0 0x618E9735 /* 01100001100011101001011100110101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME1`
```c
#define FIO_U32_HASH_PRIME1 0xD9E8E033 /* 11011001111010001110000000110011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME2`
```c
#define FIO_U32_HASH_PRIME2 0x50F116F9 /* 01010000111100010001011011111001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME3`
```c
#define FIO_U32_HASH_PRIME3 0x6E098F4B /* 01101110000010011000111101001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME4`
```c
#define FIO_U32_HASH_PRIME4 0x8CC87A6B /* 10001100110010000111101001101011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME5`
```c
#define FIO_U32_HASH_PRIME5 0x59E16F03 /* 01011001111000010110111100000011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME6`
```c
#define FIO_U32_HASH_PRIME6 0xBB838C63 /* 10111011100000111000110001100011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME7`
```c
#define FIO_U32_HASH_PRIME7 0x8532FF05 /* 10000101001100101111111100000101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME8`
```c
#define FIO_U32_HASH_PRIME8 0x44FEC4A5 /* 01000100111111101100010010100101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME9`
```c
#define FIO_U32_HASH_PRIME9 0x9B3350D5 /* 10011011001100110101000011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME10`
```c
#define FIO_U32_HASH_PRIME10 0x64BE6287 /* 01100100101111100110001010000111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME11`
```c
#define FIO_U32_HASH_PRIME11 0x57C2370B /* 01010111110000100011011100001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME12`
```c
#define FIO_U32_HASH_PRIME12 0x9E724F41 /* 10011110011100100100111101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME13`
```c
#define FIO_U32_HASH_PRIME13 0xF4A8A173 /* 11110100101010001010000101110011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME14`
```c
#define FIO_U32_HASH_PRIME14 0x6C4560FD /* 01101100010001010110000011111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME15`
```c
#define FIO_U32_HASH_PRIME15 0xD8558C3D /* 11011000010101011000110000111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME16`
```c
#define FIO_U32_HASH_PRIME16 0xC2F29157 /* 11000010111100101001000101010111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME17`
```c
#define FIO_U32_HASH_PRIME17 0xF4D03789 /* 11110100110100000011011110001001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME18`
```c
#define FIO_U32_HASH_PRIME18 0x9FB01857 /* 10011111101100000001100001010111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME19`
```c
#define FIO_U32_HASH_PRIME19 0xE9513C0F /* 11101001010100010011110000001111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME20`
```c
#define FIO_U32_HASH_PRIME20 0x89862FD3 /* 10001001100001100010111111010011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME21`
```c
#define FIO_U32_HASH_PRIME21 0xB742A51D /* 10110111010000101010010100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME22`
```c
#define FIO_U32_HASH_PRIME22 0xB3A681B9 /* 10110011101001101000000110111001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME23`
```c
#define FIO_U32_HASH_PRIME23 0xC44899F7 /* 11000100010010001001100111110111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME24`
```c
#define FIO_U32_HASH_PRIME24 0x67DE8341 /* 01100111110111101000001101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME25`
```c
#define FIO_U32_HASH_PRIME25 0xF453213B /* 11110100010100110010000100111011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME26`
```c
#define FIO_U32_HASH_PRIME26 0xD22F9855 /* 11010010001011111001100001010101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME27`
```c
#define FIO_U32_HASH_PRIME27 0x8B3E807D /* 10001011001111101000000001111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME28`
```c
#define FIO_U32_HASH_PRIME28 0x59DD1C23 /* 01011001110111010001110000100011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME29`
```c
#define FIO_U32_HASH_PRIME29 0xEE548C8B /* 11101110010101001000110010001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME30`
```c
#define FIO_U32_HASH_PRIME30 0xD2E74E05 /* 11010010111001110100111000000101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME31`
```c
#define FIO_U32_HASH_PRIME31 0x4E55788D /* 01001110010101010111100010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME0`
```c
#define FIO_U64_HASH_PRIME0 ((uint64_t)0x39664DEECA23D825) /* 0011100101100110010011011110111011001010001000111101100000100101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME1`
```c
#define FIO_U64_HASH_PRIME1 ((uint64_t)0x48644F7B3959621F) /* 0100100001100100010011110111101100111001010110010110001000011111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME2`
```c
#define FIO_U64_HASH_PRIME2 ((uint64_t)0x613A19F5CB0D98D5) /* 0110000100111010000110011111010111001011000011011001100011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME3`
```c
#define FIO_U64_HASH_PRIME3 ((uint64_t)0x84B56B93C869EA0F) /* 1000010010110101011010111001001111001000011010011110101000001111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME4`
```c
#define FIO_U64_HASH_PRIME4 ((uint64_t)0x8EE38D13E0D95A8D) /* 1000111011100011100011010001001111100000110110010101101010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME5`
```c
#define FIO_U64_HASH_PRIME5 ((uint64_t)0x92E99EC981F0E279) /* 1001001011101001100111101100100110000001111100001110001001111001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME6`
```c
#define FIO_U64_HASH_PRIME6 ((uint64_t)0xDDC3100BEF158BB1) /* 1101110111000011000100000000101111101111000101011000101110110001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME7`
```c
#define FIO_U64_HASH_PRIME7 ((uint64_t)0x918F4D38049F78BD) /* 1001000110001111010011010011100000000100100111110111100010111101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME8`
```c
#define FIO_U64_HASH_PRIME8 ((uint64_t)0xB6C9F8032A35E2D9) /* 1011011011001001111110000000001100101010001101011110001011011001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME9`
```c
#define FIO_U64_HASH_PRIME9 ((uint64_t)0xFA2A5F16D2A128D5) /* 1111101000101010010111110001011011010010101000010010100011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME10`
```c
#define FIO_U64_HASH_PRIME10 ((uint64_t)0x5823C958ED5547D9) /* 0101100000100011110010010101100011101101010101010100011111011001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME11`
```c
#define FIO_U64_HASH_PRIME11 ((uint64_t)0xE8AB702EEE09CB43) /* 1110100010101011011100000010111011101110000010011100101101000011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME12`
```c
#define FIO_U64_HASH_PRIME12 ((uint64_t)0xEBD609356421F13D) /* 1110101111010110000010010011010101100100001000011111000100111101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME13`
```c
#define FIO_U64_HASH_PRIME13 ((uint64_t)0x43D0C330AF5B1F17) /* 0100001111010000110000110011000010101111010110110001111100010111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME14`
```c
#define FIO_U64_HASH_PRIME14 ((uint64_t)0xFEAE66D234871807) /* 1111111010101110011001101101001000110100100001110001100000000111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME15`
```c
#define FIO_U64_HASH_PRIME15 ((uint64_t)0xEE54B43A52941D6B) /* 1110111001010100101101000011101001010010100101000001110101101011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME16`
```c
#define FIO_U64_HASH_PRIME16 ((uint64_t)0x874E9DE46F15E205) /* 1000011101001110100111011110010001101111000101011110001000000101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME17`
```c
#define FIO_U64_HASH_PRIME17 ((uint64_t)0xFC7CA51A8A2E9171) /* 1111110001111100101001010001101010001010001011101001000101110001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME18`
```c
#define FIO_U64_HASH_PRIME18 ((uint64_t)0x83A70617F71F3C21) /* 1000001110100111000001100001011111110111000111110011110000100001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME19`
```c
#define FIO_U64_HASH_PRIME19 ((uint64_t)0x50FA705D6D99C11D) /* 0101000011111010011100000101110101101101100110011100000100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME20`
```c
#define FIO_U64_HASH_PRIME20 ((uint64_t)0x5362B5E6CF64814B) /* 0101001101100010101101011110011011001111011001001000000101001011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME21`
```c
#define FIO_U64_HASH_PRIME21 ((uint64_t)0xA7A178389B0F3077) /* 1010011110100001011110000011100010011011000011110011000001110111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME22`
```c
#define FIO_U64_HASH_PRIME22 ((uint64_t)0x779D78921199BA45) /* 0111011110011101011110001001001000010001100110011011101001000101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME23`
```c
#define FIO_U64_HASH_PRIME23 ((uint64_t)0xB42FD16A9AE90F81) /* 1011010000101111110100010110101010011010111010010000111110000001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME24`
```c
#define FIO_U64_HASH_PRIME24 ((uint64_t)0xA2B4538A3C95576D) /* 1010001010110100010100111000101000111100100101010101011101101101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME25`
```c
#define FIO_U64_HASH_PRIME25 ((uint64_t)0x5E23D8E445F94E0D) /* 0101111000100011110110001110010001000101111110010100111000001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME26`
```c
#define FIO_U64_HASH_PRIME26 ((uint64_t)0xE7CA493CD6444F07) /* 1110011111001010010010010011110011010110010001000100111100000111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME27`
```c
#define FIO_U64_HASH_PRIME27 ((uint64_t)0x734719A6A1873CB5) /* 0111001101000111000110011010011010100001100001110011110010110101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME28`
```c
#define FIO_U64_HASH_PRIME28 ((uint64_t)0x56CCB954143A3AB7) /* 0101011011001100101110010101010000010100001110100011101010110111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME29`
```c
#define FIO_U64_HASH_PRIME29 ((uint64_t)0xFA5BC5A72480BF81) /* 1111101001011011110001011010011100100100100000001011111110000001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME30`
```c
#define FIO_U64_HASH_PRIME30 ((uint64_t)0xD9B97D090C09F789) /* 1101100110111001011111010000100100001100000010011111011110001001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME31`
```c
#define FIO_U64_HASH_PRIME31 ((uint64_t)0x9F74D0B9972E404B) /* 1001111101110100110100001011100110010111001011100100000001001011 */
```
_Symbol type:_ `macro`
#### `FIO_PRIME_TABLE_LIMIT`
```c
#define FIO_PRIME_TABLE_LIMIT 1024
```
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP`
```c
#define FIO_MATH_UXXX_OP(t, a, b, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = (a)[i__] op(b)[i__]; \
} while (0)
```
Performs `a op b` (+,-, *, etc') using easily vectorized loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_COP`
```c
#define FIO_MATH_UXXX_COP(t, a, b, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = (a)[i__] op(b); \
} while (0)
```
Performs `a op b` (+,-, *, etc'), where `b` is a constant.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_SOP`
```c
#define FIO_MATH_UXXX_SOP(t, a, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = op(a)[i__]; \
} while (0)
```
Performs `t = op (a)`.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_RROT`
```c
#define FIO_MATH_UXXX_OP_RROT(t, a, b, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = \
((a)[i__] >> ((b)[i__] & ((bits)-1))) | \
((a)[i__] << (((bits) - ((b)[i__] & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a >> b) | (a << (bits - b))` (right rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_CRROT`
```c
#define FIO_MATH_UXXX_OP_CRROT(t, a, c, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = ((a)[i__] >> ((c) & ((bits)-1))) | \
((a)[i__] << (((bits) - ((c) & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a >> c) | (a << (bits - c))` (const right rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_LROT`
```c
#define FIO_MATH_UXXX_OP_LROT(t, a, b, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = \
((a)[i__] << ((b)[i__] & ((bits)-1))) | \
((a)[i__] >> (((bits) - ((b)[i__] & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a << b) | (a >> (bits - b))` (left rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_CLROT`
```c
#define FIO_MATH_UXXX_OP_CLROT(t, a, c, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = ((a)[i__] << ((c) & ((bits)-1))) | \
((a)[i__] >> (((bits) - ((c) & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a << c) | (a >> (bits - c))` (const left rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_TOP`
```c
#define FIO_MATH_UXXX_TOP(t, a, b, c, bits, expr) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = expr((a)[i__], (b)[i__], (c)[i__]); \
} while (0)
```
Performs ternary `t = f(a, b, c)` lane-wise using easily vectorized loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_REDUCE`
```c
#define FIO_MATH_UXXX_REDUCE(t, a, bits, op) \
do { \
t = (a)[0]; \
for (size_t i__ = 1; i__ < (sizeof((a)) / sizeof((a)[0])); ++i__) \
(t) = (t)op(a)[i__]; \
} while (0)
```
Performs vector reduction for using `op` (+,-, *, etc'), storing to `t`.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_SUFFLE`
```c
#define FIO_MATH_UXXX_SUFFLE(var, bits, ...) \
do { \
uint##bits##_t t____[sizeof((var)) / sizeof((var)[0])]; \
const uint8_t shuf____[sizeof((var)) / sizeof((var)[0])] = {__VA_ARGS__}; \
for (size_t i___ = 0; i___ < (sizeof((var)) / sizeof((var)[0])); ++i___) \
t____[i___] = (var)[shuf____[i___]]; \
for (size_t i___ = 0; i___ < (sizeof((var)) / sizeof((var)[0])); ++i___) \
(var)[i___] = t____[i___]; \
} while (0)
```
Performs vector shuffling (reordering) of `var`.
_Symbol type:_ `macro`
#### `FIO_VEC_SIMPLE_OP`
```c
#define FIO_VEC_SIMPLE_OP(result, a, b, len, op) \
FIO_FOR_UNROLL(len, sizeof(*a), i__, result[i__] = a[i__] op b[i__])
```
A math operation `op` between correlating vector positions of same length
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_OP`
```c
#define FIO_VEC_SCALAR_OP(result, v, sclr, len, op) \
FIO_FOR_UNROLL(len, sizeof(*(v)), i__, (result)[i__] = (v)[i__] op(sclr))
```
A math operation `op` between a scalar and a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_OP`
```c
#define FIO_VEC_REDUCE_OP(result, vec, len, op) \
FIO_FOR_UNROLL(len, sizeof(*vec), i__, result = result op vec[i__])
```
A math operation `op` between all vector members
_Symbol type:_ `macro`
#### `FIO_VEC_DOT`
```c
#define FIO_VEC_DOT(result, a, b, len) \
FIO_FOR_UNROLL(len, sizeof(*a), i__, result += a[i__] * b[i__])
```
The dot product of two vectors (sum(a[i]*b[i])).
_Symbol type:_ `macro`
#### `FIO_VEC_ADD`
```c
#define FIO_VEC_ADD(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, +)
```
Add two vectors of same length (adding corresponding positions)
_Symbol type:_ `macro`
#### `FIO_VEC_SUB`
```c
#define FIO_VEC_SUB(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, -)
```
Subtracting two vectors of same length
_Symbol type:_ `macro`
#### `FIO_VEC_MUL`
```c
#define FIO_VEC_MUL(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, *)
```
Multiplying two vectors (multiplying corresponding positions - half dot)
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_ADD`
```c
#define FIO_VEC_SCALAR_ADD(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, +)
```
Add scalar to vector
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_SUB`
```c
#define FIO_VEC_SCALAR_SUB(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, -)
```
Subtract scalar from vector
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_MUL`
```c
#define FIO_VEC_SCALAR_MUL(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, *)
```
Multiply scalar from vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_ADD`
```c
#define FIO_VEC_REDUCE_ADD(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, +)
```
Add all members of a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_SUB`
```c
#define FIO_VEC_REDUCE_SUB(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, -)
```
Subtract all members of a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_MUL`
```c
#define FIO_VEC_REDUCE_MUL(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, *)
```
Multiply all members of a vector
_Symbol type:_ `macro`
#### `FIO_DEFINE_RANDOM128_FN`
```c
#define FIO_DEFINE_RANDOM128_FN(extern, name, reseed_log, seed_offset) \
static uint64_t name##___state[9] FIO_ALIGN(64) = { \
0x9c65875be1fce7b9ULL + seed_offset, \
0x7cc568e838f6a40dULL, \
0x4bb8d885a0fe47d5ULL + seed_offset, \
0x95561f0927ad7ecdULL, \
0}; \
extern FIO_MAYBE_UNUSED void name##_reset(void) { \
name##___state[0] = 0x9c65875be1fce7b9ULL + seed_offset; \
name##___state[1] = 0x7cc568e838f6a40dULL; \
name##___state[2] = 0x4bb8d885a0fe47d5ULL + seed_offset; \
name##___state[3] = 0x95561f0927ad7ecdULL; \
name##___state[8] = name##___state[4] = 0; \
} \
extern void name##_reseed(void) { \
const size_t jitter_samples = 16 | (name##___state[0] & 15); \
for (size_t i = 0; i < jitter_samples; ++i) { \
struct timespec t; \
clock_gettime(CLOCK_MONOTONIC, &t); \
uint64_t clk[2] = {(uint64_t)(uintptr_t)&clk + (uint64_t)(uintptr_t) & \
(name##_reseed), \
0}; \
clk[0] += (uint64_t)((t.tv_sec << 30) + (int64_t)t.tv_nsec); \
clk[0] = fio_math_mulc64(clk[0], FIO_U64_HASH_PRIME0, clk + 1); \
clk[1] += FIO_U64_HASH_PRIME0; \
clk[0] += fio_lrot64(clk[0], 27); \
clk[0] += fio_lrot64(clk[0], 49); \
clk[1] += fio_lrot64(clk[1], 27); \
clk[1] += fio_lrot64(clk[1], 49); \
name##___state[0] += clk[0] + fio_cycle_counter(); \
name##___state[1] += clk[1] + fio_cycle_counter(); \
name##___state[2] += clk[0] + fio_cycle_counter(); \
name##___state[3] += clk[1] + fio_cycle_counter(); \
} \
name##___state[8] += ((name##___state[8] & 7) + (name##___state[0] & 30)); \
} \
/** Re-seeds the PNGR so forked processes don't match. */ \
extern FIO_MAYBE_UNUSED void name##_on_fork(void *is_null) { \
(void)is_null; \
name##_reseed(); \
} \
/** Returns a 128 bit pseudo-random number. */ \
extern FIO_MAYBE_UNUSED fio_u128 name##128(void) { \
fio_u128 r; \
uint64_t s0[4]; \
const uint64_t counter = fio_atomic_add(name##___state + 4, 1); \
if (!(counter & ((1ULL << reseed_log) - 1)) && \
((size_t)(reseed_log - 1) < 63)) \
name##_reseed(); \
{ /* load state + per-call input (timing, counter, address variation) */ \
const uint64_t cycles = \
reseed_log ? fio_cycle_counter() + (uint64_t)(uintptr_t)&cycles \
: 0xB5ULL; \
const uint64_t variation = \
0x4E55788DULL + \
(reseed_log ? (uint64_t)(uintptr_t)&name##_reseed : 0); \
s0[0] = name##___state[0] + cycles + counter; \
s0[1] = name##___state[1] + cycles + (seed_offset + 0x59DD1C23ULL); \
s0[2] = name##___state[2] + cycles + counter; \
s0[3] = name##___state[3] + cycles + variation; \
} \
{ /* update: one Feistel layer (lanes 0,2 from 1,3), then rotate lanes */ \
const uint64_t a_ = \
s0[0] + fio_math_mul64_fold(s0[1] ^ FIO_U64_HASH_PRIME0, \
s0[3] ^ FIO_U64_HASH_PRIME1); \
const uint64_t c_ = \
s0[2] + fio_math_mul64_fold(s0[3] ^ FIO_U64_HASH_PRIME2, \
s0[1] ^ FIO_U64_HASH_PRIME3); \
s0[0] = s0[1]; /* rotate: the other pair is updated next call */ \
s0[1] = c_; \
s0[2] = s0[3]; \
s0[3] = a_; \
} \
/* output: two multiply layers over all lanes (never stored) */ \
r.u64[0] = fio_math_mul64_fold(s0[0] ^ FIO_U64_HASH_PRIME8, \
s0[1] ^ FIO_U64_HASH_PRIME9) + \
fio_math_mul64_fold(s0[2] ^ FIO_U64_HASH_PRIME10, \
s0[3] ^ FIO_U64_HASH_PRIME11); \
r.u64[1] = fio_math_mul64_fold(s0[0] ^ FIO_U64_HASH_PRIME12, \
s0[3] ^ FIO_U64_HASH_PRIME13) + \
fio_math_mul64_fold(s0[1] ^ FIO_U64_HASH_PRIME14, \
s0[2] ^ FIO_U64_HASH_PRIME15); \
for (size_t i = 0; i < 4; ++i) /* store to memory */ \
name##___state[i] = s0[i]; \
return r; \
} \
/** Returns a 64 bit pseudo-random number. */ \
extern FIO_MAYBE_UNUSED uint64_t name##64(void) { \
static fio_u128 r; \
size_t counter = fio_atomic_add(name##___state + 8, 1); \
if (!(counter & 1)) \
r = name##128(); \
return r.u64[counter & 1]; \
} \
/** Fills the `dest` buffer with pseudo-random noise. */ \
extern FIO_MAYBE_UNUSED void name##_bytes(void *dest, size_t len) { \
if (!dest || !len) \
return; \
uint8_t *d = (uint8_t *)dest; \
for (unsigned i = 15; i < len; i += 16) { \
fio_u128 r = name##128(); \
fio_memcpy16(d, r.u8); \
d += 16; \
} \
if (len & 15) { \
fio_u128 r = name##128(); \
fio_memcpy15x(d, r.u8, len); \
} \
}
```
Defines a semi-deterministic Pseudo-Random 128 bit Number Generator function.
The following functions will be defined:
- extern fio_u128 name##128(void); // returns 128 bits
- extern uint64_t name##64(void); // returns 64 bits (simply half the result)
- extern void name##_bytes(void *buffer, size_t len); // fills a buffer
- extern void name##_reset(void); // resets the state of the PRNG
- extern void name##_on_fork(void * is_null); // reseeds the PRNG
If `reseed_log` is non-zero and less than 64, the PNGR is no longer
deterministic, as it will automatically re-seeds itself every 2^reseed_log
iterations.
If `extern` is `static` or `FIO_SFUNC`, static function will be defined.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_IS_EQ`
```c
#define FIO_STR_INFO_IS_EQ(s1, s2) \
((s1).len == (s2).len && \
(!(s1).len || (s1).buf == (s2).buf || \
((s1).buf && (s2).buf && (s1).buf[0] == (s2).buf[0] && \
!FIO_MEMCMP((s1).buf, (s2).buf, (s1).len))))
```
Compares two `fio_str_info_s` objects for content equality.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO_IS_EQ`
```c
#define FIO_BUF_INFO_IS_EQ(s1, s2) FIO_STR_INFO_IS_EQ((s1), (s2))
```
Compares two `fio_buf_info_s` objects for content equality.
_Symbol type:_ `macro`
#### `FIO_STR_INFO0`
```c
#define FIO_STR_INFO0 ((fio_str_info_s){0})
```
A NULL fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO1`
```c
#define FIO_STR_INFO1(str) \
((fio_str_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO2`
```c
#define FIO_STR_INFO2(str, length) \
((fio_str_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO3`
```c
#define FIO_STR_INFO3(str, length, capacity) \
((fio_str_info_s){.len = (length), .buf = (str), .capa = (capacity)})
```
Converts a String with a known length and capacity into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO0`
```c
#define FIO_BUF_INFO0 ((fio_buf_info_s){0})
```
A NULL fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO1`
```c
#define FIO_BUF_INFO1(str) \
((fio_buf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO2`
```c
#define FIO_BUF_INFO2(str, length) \
((fio_buf_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO0`
```c
#define FIO_UBUF_INFO0 ((fio_ubuf_info_s){0})
```
A NULL fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO1`
```c
#define FIO_UBUF_INFO1(str) \
((fio_ubuf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO2`
```c
#define FIO_UBUF_INFO2(str, length) \
((fio_ubuf_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF2STR_INFO`
```c
#define FIO_BUF2STR_INFO(buf_info) \
((fio_str_info_s){.len = (buf_info).len, .buf = (buf_info).buf})
```
Converts a fio_buf_info_s into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR2BUF_INFO`
```c
#define FIO_STR2BUF_INFO(str_info) \
((fio_buf_info_s){.len = (str_info).len, .buf = (str_info).buf})
```
Converts a fio_buf_info_s into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_TMP_VAR`
```c
#define FIO_STR_INFO_TMP_VAR(name, capacity) \
char fio___stack_mem___##name[(capacity) + 1]; \
fio___stack_mem___##name[0] = 0; /* guard */ \
fio_str_info_s name = (fio_str_info_s) { \
.buf = fio___stack_mem___##name, .capa = (capacity) \
}
```
Creates a stack fio_str_info_s variable `name` with `capacity` bytes.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_TMP_IS_REALLOCATED`
```c
#define FIO_STR_INFO_TMP_IS_REALLOCATED(name) \
(fio___stack_mem___##name != name.buf)
```
Tests to see if memory reallocation happened.
_Symbol type:_ `macro`
#### `FIO_UTF8_ALLOW_IF`
```c
#define FIO_UTF8_ALLOW_IF 1
```
_Symbol type:_ `macro`
### Types
#### `fio_lock_i`
```c
typedef volatile unsigned char fio_lock_i
```
A spin lock type, must be initialized to `FIO_LOCK_INIT` before use.
_Symbol type:_ `type`
#### `fio_list_node_s`
```c
struct fio_list_node_s {
struct fio_list_node_s *next;
struct fio_list_node_s *prev;
}
```
A linked list arch-type
_Symbol type:_ `type`
#### `fio_index32_node_s`
```c
struct fio_index32_node_s {
uint32_t next;
uint32_t prev;
}
```
A 32 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_index16_node_s`
```c
struct fio_index16_node_s {
uint16_t next;
uint16_t prev;
}
```
A 16 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_index8_node_s`
```c
struct fio_index8_node_s {
uint8_t next;
uint8_t prev;
}
```
An 8 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_u128`
```c
union fio_u128 {
FIO___UXXX_UGRP_DEF(128);
#if defined(__SIZEOF_INT128__)
__uint128_t alignment_for_u128_[1];
#endif
}
```
An unsigned 128bit union type.
_Symbol type:_ `type`
#### `fio_u256`
```c
union fio_u256 {
fio_u128 u128[2];
FIO___UXXX_UGRP_DEF(256);
#if defined(__SIZEOF_INT256__)
__uint256_t alignment_for_u256_[1];
#endif
}
```
An unsigned 256bit union type.
_Symbol type:_ `type`
#### `fio_u512`
```c
union fio_u512 {
fio_u128 u128[4];
fio_u256 u256[2];
FIO___UXXX_UGRP_DEF(512);
}
```
An unsigned 512bit union type.
_Symbol type:_ `type`
#### `fio_u1024`
```c
union fio_u1024 {
fio_u128 u128[8];
fio_u256 u256[4];
fio_u512 u512[2];
FIO___UXXX_UGRP_DEF(1024);
}
```
An unsigned 1024bit union type.
_Symbol type:_ `type`
#### `fio_u2048`
```c
union fio_u2048 {
fio_u128 u128[16];
fio_u256 u256[8];
fio_u512 u512[4];
fio_u1024 u1024[2];
FIO___UXXX_UGRP_DEF(2048);
}
```
An unsigned 2048bit union type.
_Symbol type:_ `type`
#### `fio_u4096`
```c
union fio_u4096 {
fio_u128 u128[32];
fio_u256 u256[16];
fio_u512 u512[8];
fio_u1024 u1024[4];
fio_u2048 u2048[2];
FIO___UXXX_UGRP_DEF(4096);
}
```
An unsigned 4096bit union type.
_Symbol type:_ `type`
#### `fio_str_info_s`
```c
struct fio_str_info_s {
/** The string's length, if any. */
size_t len;
/** The string's buffer (pointer to first byte) or NULL on error. */
char *buf;
/** The buffer's capacity. Zero (0) indicates the buffer is read-only. */
size_t capa;
}
```
An information type for reporting the string's state.
_Symbol type:_ `type`
#### `fio_buf_info_s`
```c
struct fio_buf_info_s {
/** The buffer's length, if any. */
size_t len;
/** The buffer's address (may be NULL if no buffer). */
char *buf;
}
```
An information type for reporting/storing buffer data (no `capa`).
_Symbol type:_ `type`
#### `fio_ubuf_info_s`
```c
struct fio_ubuf_info_s {
/** The buffer's length, if any. */
size_t len;
/** The buffer's address (may be NULL if no buffer). */
unsigned char *buf;
}
```
An information type for reporting/storing unsigned buffer data.
_Symbol type:_ `type`
### Functions
#### `fio_getpid`
```c
#define fio_getpid _getpid
```
Calls the `getpid` provided by the system.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_load`
```c
#define fio_atomic_load(dest, p_obj) \
do { \
dest = __atomic_load_n((p_obj), __ATOMIC_SEQ_CST); \
} while (0)
```
An atomic load operation, returns value in pointer.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_compare_exchange_p`
```c
#define fio_atomic_compare_exchange_p(p_obj, p_expected, p_desired) __atomic_compare_exchange((p_obj), (p_expected), (p_desired), 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
```
An atomic compare and exchange operation, returns true if an exchange occurred. `p_expected` MAY be overwritten with the existing value (system specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_exchange`
```c
#define fio_atomic_exchange(p_obj, value) __atomic_exchange_n((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic exchange operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_add`
```c
#define fio_atomic_add(p_obj, value) __atomic_fetch_add((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic addition operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_sub`
```c
#define fio_atomic_sub(p_obj, value) __atomic_fetch_sub((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic subtraction operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_and`
```c
#define fio_atomic_and(p_obj, value) __atomic_fetch_and((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic AND (&) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_xor`
```c
#define fio_atomic_xor(p_obj, value) __atomic_fetch_xor((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic XOR (^) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_or`
```c
#define fio_atomic_or(p_obj, value) __atomic_fetch_or((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic OR (|) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_add_fetch`
```c
#define fio_atomic_add_fetch(p_obj, value) __atomic_add_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic addition operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_sub_fetch`
```c
#define fio_atomic_sub_fetch(p_obj, value) __atomic_sub_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic subtraction operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_and_fetch`
```c
#define fio_atomic_and_fetch(p_obj, value) __atomic_and_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic AND (&) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_xor_fetch`
```c
#define fio_atomic_xor_fetch(p_obj, value) __atomic_xor_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic XOR (^) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_or_fetch`
```c
#define fio_atomic_or_fetch(p_obj, value) __atomic_or_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic OR (|) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_trylock_group`
```c
inline uint8_t fio_trylock_group(fio_lock_i *lock, uint8_t group)
```
Tries to lock a group of sublocks.
Combine a number of sublocks using OR (`|`) and the FIO_LOCK_SUBLOCK(i)
macro. i.e.:
```c
if(!fio_trylock_group(&lock,
FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2))) {
// act in lock
}
fio_unlock_group(&lock, FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2)));
```
Returns 0 on success and non-zero on failure.
_Symbol type:_ `function`
#### `fio_lock_group`
```c
inline void fio_lock_group(fio_lock_i *lock, uint8_t group)
```
Busy waits for a group lock to become available - not recommended.
See `fio_trylock_group` for details.
_Symbol type:_ `function`
#### `fio_unlock_group`
```c
inline void fio_unlock_group(fio_lock_i *lock, uint8_t group)
```
Unlocks a sublock group, no matter which thread owns which sublock.
_Symbol type:_ `function`
#### `fio_trylock_full`
```c
inline uint8_t fio_trylock_full(fio_lock_i *lock)
```
Tries to lock all sublocks. Returns 0 on success and 1 on failure.
_Symbol type:_ `function`
#### `fio_lock_full`
```c
inline void fio_lock_full(fio_lock_i *lock)
```
Busy waits for all sub lock to become available - not recommended.
_Symbol type:_ `function`
#### `fio_unlock_full`
```c
inline void fio_unlock_full(fio_lock_i *lock)
```
Unlocks all sub locks, no matter which thread owns the lock.
_Symbol type:_ `function`
#### `fio_trylock`
```c
inline uint8_t fio_trylock(fio_lock_i *lock)
```
Tries to acquire the default lock (sublock 0).
Returns 0 on success and 1 on failure.
_Symbol type:_ `function`
#### `fio_lock`
```c
inline void fio_lock(fio_lock_i *lock)
```
Busy waits for the default lock to become available - not recommended.
_Symbol type:_ `function`
#### `fio_unlock`
```c
inline void fio_unlock(fio_lock_i *lock)
```
Unlocks the default lock, no matter which thread owns the lock.
_Symbol type:_ `function`
#### `fio_is_locked`
```c
inline uint8_t fio_is_locked(fio_lock_i *lock)
```
Returns 1 if the lock is locked, 0 otherwise.
_Symbol type:_ `function`
#### `fio_is_group_locked`
```c
inline uint8_t fio_is_group_locked(fio_lock_i *lock, uint8_t group)
```
Returns 1 if the lock is locked, 0 otherwise.
_Symbol type:_ `function`
#### `fio_atomic_bit_get`
```c
inline uint8_t fio_atomic_bit_get(void *map, size_t bit)
```
Gets the state of a bit in a bitmap.
_Symbol type:_ `function`
#### `fio_atomic_bit_set`
```c
inline void fio_atomic_bit_set(void *map, size_t bit)
```
Sets the a bit in a bitmap (sets to 1).
_Symbol type:_ `function`
#### `fio_atomic_bit_unset`
```c
inline void fio_atomic_bit_unset(void *map, size_t bit)
```
Unsets the a bit in a bitmap (sets to 0).
_Symbol type:_ `function`
#### `fio_atomic_bit_flip`
```c
inline void fio_atomic_bit_flip(void *map, size_t bit)
```
Flips the a bit in a bitmap (sets to 0 if 1, sets to 1 if 0).
_Symbol type:_ `function`
#### `fio_is_little_endian`
```c
inline unsigned int fio_is_little_endian(void)
```
Dynamically test if we are on a little endian machine.
_Symbol type:_ `function`
#### `fio_is_big_endian`
```c
inline unsigned int fio_is_big_endian(void)
```
Dynamically test if we are on a big endian machine.
_Symbol type:_ `function`
#### `fio_memcpy0`
```c
FIO_SFUNC void *fio_memcpy0(void *restrict d, const void *restrict s)
```
No-op (completes the name space).
_Symbol type:_ `function`
#### `fio_memcpy1`
```c
void * fio_memcpy1(void *restrict d, const void *restrict s)
```
Copies 1 byte from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2`
```c
void * fio_memcpy2(void *restrict d, const void *restrict s)
```
Copies 2 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy3`
```c
void * fio_memcpy3(void *restrict d, const void *restrict s)
```
Copies 3 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4`
```c
void * fio_memcpy4(void *restrict d, const void *restrict s)
```
Copies 4 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy5`
```c
void * fio_memcpy5(void *restrict d, const void *restrict s)
```
Copies 5 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy6`
```c
void * fio_memcpy6(void *restrict d, const void *restrict s)
```
Copies 6 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy7`
```c
void * fio_memcpy7(void *restrict d, const void *restrict s)
```
Copies 7 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy8`
```c
void * fio_memcpy8(void *restrict d, const void *restrict s)
```
Copies 8 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy16`
```c
void * fio_memcpy16(void *restrict d, const void *restrict s)
```
Copies 16 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy32`
```c
void * fio_memcpy32(void *restrict d, const void *restrict s)
```
Copies 32 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy64`
```c
void * fio_memcpy64(void *restrict d, const void *restrict s)
```
Copies 64 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy128`
```c
void * fio_memcpy128(void *restrict d, const void *restrict s)
```
Copies 128 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy256`
```c
void * fio_memcpy256(void *restrict d, const void *restrict s)
```
Copies 256 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy512`
```c
void * fio_memcpy512(void *restrict d, const void *restrict s)
```
Copies 512 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy1024`
```c
void * fio_memcpy1024(void *restrict d, const void *restrict s)
```
Copies 1024 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2048`
```c
void * fio_memcpy2048(void *restrict d, const void *restrict s)
```
Copies 2048 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4096`
```c
void * fio_memcpy4096(void *restrict d, const void *restrict s)
```
Copies 4096 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy0x`
```c
FIO_SFUNC void *fio_memcpy0x(void *d, const void *s, size_t l)
```
No-op (completes the name space).
_Symbol type:_ `function`
#### `fio_memcpy7x`
```c
void * fio_memcpy7x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy15x`
```c
void * fio_memcpy15x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy31x`
```c
void * fio_memcpy31x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy63x`
```c
void * fio_memcpy63x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy127x`
```c
void * fio_memcpy127x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy255x`
```c
void * fio_memcpy255x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy511x`
```c
void * fio_memcpy511x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy1023x`
```c
void * fio_memcpy1023x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2047x`
```c
void * fio_memcpy2047x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4095x`
```c
void * fio_memcpy4095x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_bswap8`
```c
#define fio_bswap8(i) (i)
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap16`
```c
#define fio_bswap16(i) __builtin_bswap16((uint16_t)(i))
```
Byte swap a 16 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap32`
```c
#define fio_bswap32(i) __builtin_bswap32((uint32_t)(i))
```
Byte swap a 32 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap64`
```c
#define fio_bswap64(i) __builtin_bswap64((uint64_t)(i))
```
Byte swap a 64 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap128`
```c
#define fio_bswap128(i) __builtin_bswap128((__uint128_t)(i))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap128`
```c
inline FIO_CONST __uint128_t fio_bswap128(__uint128_t i)
```
_Symbol type:_ `function`
#### `fio_ltole8`
```c
#define fio_ltole8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton8`
```c
#define fio_lton8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol8`
```c
#define fio_ntol8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton16`
```c
#define fio_lton16(i) (i)
```
Local byte order to Network byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton32`
```c
#define fio_lton32(i) (i)
```
Local byte order to Network byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton64`
```c
#define fio_lton64(i) (i)
```
Local byte order to Network byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole16`
```c
#define fio_ltole16(i) fio_bswap16((i))
```
Local byte order to Little Endian byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole32`
```c
#define fio_ltole32(i) fio_bswap32((i))
```
Local byte order to Little Endian byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole64`
```c
#define fio_ltole64(i) fio_bswap64((i))
```
Local byte order to Little Endian byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol16`
```c
#define fio_ntol16(i) (i)
```
Network byte order to Local byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol32`
```c
#define fio_ntol32(i) (i)
```
Network byte order to Local byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol64`
```c
#define fio_ntol64(i) (i)
```
Network byte order to Local byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol128`
```c
#define fio_ntol128(i) (i)
```
Network byte order to Local byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole128`
```c
#define fio_ltole128(i) fio_bswap128((i))
```
Local byte order to Little Endian byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton128`
```c
#define fio_lton128(i) fio_bswap128((i))
```
Local byte order to Network byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_buf2u8u`
```c
inline uint8_t fio_buf2u8u(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8u`
```c
inline void fio_u2buf8u(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u8_le`
```c
inline uint8_t fio_buf2u8_le(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8_le`
```c
inline void fio_u2buf8_le(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u8_be`
```c
inline uint8_t fio_buf2u8_be(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8_be`
```c
inline void fio_u2buf8_be(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16u`
```c
uint16_t fio_buf2u16u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16u`
```c
void fio_u2buf16u(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32u`
```c
uint32_t fio_buf2u32u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32u`
```c
void fio_u2buf32u(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64u`
```c
uint64_t fio_buf2u64u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64u`
```c
void fio_u2buf64u(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16_le`
```c
uint16_t fio_buf2u16_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16_le`
```c
void fio_u2buf16_le(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32_le`
```c
uint32_t fio_buf2u32_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32_le`
```c
void fio_u2buf32_le(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64_le`
```c
uint64_t fio_buf2u64_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64_le`
```c
void fio_u2buf64_le(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16_be`
```c
uint16_t fio_buf2u16_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16_be`
```c
void fio_u2buf16_be(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32_be`
```c
uint32_t fio_buf2u32_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32_be`
```c
void fio_u2buf32_be(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64_be`
```c
uint64_t fio_buf2u64_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64_be`
```c
void fio_u2buf64_be(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24_le`
```c
inline uint32_t fio_buf2u24_le(const void *c)
```
Converts an unaligned byte stream to a 24 bit number.
_Symbol type:_ `function`
#### `fio_u2buf24_le`
```c
inline void fio_u2buf24_le(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24_be`
```c
inline uint32_t fio_buf2u24_be(const void *c)
```
Converts an unaligned byte stream to a 24 bit number.
_Symbol type:_ `function`
#### `fio_u2buf24_be`
```c
inline void fio_u2buf24_be(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24u`
```c
inline uint32_t fio_buf2u24u(const void *c)
```
Converts an unaligned byte stream to a 24 bit number - local endieness.
_Symbol type:_ `function`
#### `fio_u2buf24u`
```c
inline void fio_u2buf24u(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer - in local endieness.
_Symbol type:_ `function`
#### `fio_buf2zu`
```c
inline size_t fio_buf2zu(const void *c)
```
Converts an unaligned byte stream to a size_t - local endieness.
_Symbol type:_ `function`
#### `fio_u2bufzu`
```c
inline void fio_u2bufzu(void *buf, size_t i)
```
Writes a size_t to an unaligned buffer - in local endieness.
_Symbol type:_ `function`
#### `fio_u8x4_add`
```c
void fio_u8x4_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_and`
```c
void fio_u8x4_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_mul`
```c
void fio_u8x4_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_or`
```c
void fio_u8x4_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_add`
```c
uint8_t fio_u8x4_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_and`
```c
uint8_t fio_u8x4_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_max`
```c
uint8_t fio_u8x4_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_min`
```c
uint8_t fio_u8x4_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_mul`
```c
uint8_t fio_u8x4_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_or`
```c
uint8_t fio_u8x4_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_sub`
```c
uint8_t fio_u8x4_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_xor`
```c
uint8_t fio_u8x4_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reshuffle`
```c
void fio_u8x4_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x4_sub`
```c
void fio_u8x4_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_xor`
```c
void fio_u8x4_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_add`
```c
void fio_u8x8_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_and`
```c
void fio_u8x8_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_mul`
```c
void fio_u8x8_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_or`
```c
void fio_u8x8_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_add`
```c
uint8_t fio_u8x8_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_and`
```c
uint8_t fio_u8x8_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_max`
```c
uint8_t fio_u8x8_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_min`
```c
uint8_t fio_u8x8_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_mul`
```c
uint8_t fio_u8x8_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_or`
```c
uint8_t fio_u8x8_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_sub`
```c
uint8_t fio_u8x8_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_xor`
```c
uint8_t fio_u8x8_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reshuffle`
```c
void fio_u8x8_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x8_sub`
```c
void fio_u8x8_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_xor`
```c
void fio_u8x8_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_add`
```c
void fio_u8x16_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_and`
```c
void fio_u8x16_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_mul`
```c
void fio_u8x16_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_or`
```c
void fio_u8x16_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_add`
```c
uint8_t fio_u8x16_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_and`
```c
uint8_t fio_u8x16_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_max`
```c
uint8_t fio_u8x16_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_min`
```c
uint8_t fio_u8x16_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_mul`
```c
uint8_t fio_u8x16_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_or`
```c
uint8_t fio_u8x16_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_sub`
```c
uint8_t fio_u8x16_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_xor`
```c
uint8_t fio_u8x16_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reshuffle`
```c
void fio_u8x16_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x16_sub`
```c
void fio_u8x16_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_xor`
```c
void fio_u8x16_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_add`
```c
void fio_u8x32_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_and`
```c
void fio_u8x32_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_mul`
```c
void fio_u8x32_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_or`
```c
void fio_u8x32_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_add`
```c
uint8_t fio_u8x32_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_and`
```c
uint8_t fio_u8x32_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_max`
```c
uint8_t fio_u8x32_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_min`
```c
uint8_t fio_u8x32_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_mul`
```c
uint8_t fio_u8x32_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_or`
```c
uint8_t fio_u8x32_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_sub`
```c
uint8_t fio_u8x32_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_xor`
```c
uint8_t fio_u8x32_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reshuffle`
```c
void fio_u8x32_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x32_sub`
```c
void fio_u8x32_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_xor`
```c
void fio_u8x32_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_add`
```c
void fio_u8x64_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_and`
```c
void fio_u8x64_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_mul`
```c
void fio_u8x64_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_or`
```c
void fio_u8x64_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_add`
```c
uint8_t fio_u8x64_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_and`
```c
uint8_t fio_u8x64_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_max`
```c
uint8_t fio_u8x64_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_min`
```c
uint8_t fio_u8x64_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_mul`
```c
uint8_t fio_u8x64_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_or`
```c
uint8_t fio_u8x64_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_sub`
```c
uint8_t fio_u8x64_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_xor`
```c
uint8_t fio_u8x64_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reshuffle`
```c
void fio_u8x64_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x64_sub`
```c
void fio_u8x64_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_xor`
```c
void fio_u8x64_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_add`
```c
void fio_u8x128_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_and`
```c
void fio_u8x128_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_mul`
```c
void fio_u8x128_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_or`
```c
void fio_u8x128_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_add`
```c
uint8_t fio_u8x128_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_and`
```c
uint8_t fio_u8x128_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_max`
```c
uint8_t fio_u8x128_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_min`
```c
uint8_t fio_u8x128_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_mul`
```c
uint8_t fio_u8x128_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_or`
```c
uint8_t fio_u8x128_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_sub`
```c
uint8_t fio_u8x128_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_xor`
```c
uint8_t fio_u8x128_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reshuffle`
```c
void fio_u8x128_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x128_sub`
```c
void fio_u8x128_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_xor`
```c
void fio_u8x128_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_add`
```c
void fio_u8x256_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_and`
```c
void fio_u8x256_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_mul`
```c
void fio_u8x256_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_or`
```c
void fio_u8x256_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_add`
```c
uint8_t fio_u8x256_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_and`
```c
uint8_t fio_u8x256_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_max`
```c
uint8_t fio_u8x256_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_min`
```c
uint8_t fio_u8x256_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_mul`
```c
uint8_t fio_u8x256_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_or`
```c
uint8_t fio_u8x256_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_sub`
```c
uint8_t fio_u8x256_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_xor`
```c
uint8_t fio_u8x256_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reshuffle`
```c
void fio_u8x256_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x256_sub`
```c
void fio_u8x256_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_xor`
```c
void fio_u8x256_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_add`
```c
void fio_u16x2_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_and`
```c
void fio_u16x2_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_mul`
```c
void fio_u16x2_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_or`
```c
void fio_u16x2_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_add`
```c
uint16_t fio_u16x2_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_and`
```c
uint16_t fio_u16x2_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_max`
```c
uint16_t fio_u16x2_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_min`
```c
uint16_t fio_u16x2_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_mul`
```c
uint16_t fio_u16x2_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_or`
```c
uint16_t fio_u16x2_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_sub`
```c
uint16_t fio_u16x2_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_xor`
```c
uint16_t fio_u16x2_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reshuffle`
```c
void fio_u16x2_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x2_sub`
```c
void fio_u16x2_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_xor`
```c
void fio_u16x2_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_add`
```c
void fio_u16x4_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_and`
```c
void fio_u16x4_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_mul`
```c
void fio_u16x4_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_or`
```c
void fio_u16x4_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_add`
```c
uint16_t fio_u16x4_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_and`
```c
uint16_t fio_u16x4_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_max`
```c
uint16_t fio_u16x4_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_min`
```c
uint16_t fio_u16x4_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_mul`
```c
uint16_t fio_u16x4_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_or`
```c
uint16_t fio_u16x4_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_sub`
```c
uint16_t fio_u16x4_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_xor`
```c
uint16_t fio_u16x4_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reshuffle`
```c
void fio_u16x4_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x4_sub`
```c
void fio_u16x4_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_xor`
```c
void fio_u16x4_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_add`
```c
void fio_u16x8_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_and`
```c
void fio_u16x8_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_mul`
```c
void fio_u16x8_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_or`
```c
void fio_u16x8_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_add`
```c
uint16_t fio_u16x8_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_and`
```c
uint16_t fio_u16x8_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_max`
```c
uint16_t fio_u16x8_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_min`
```c
uint16_t fio_u16x8_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_mul`
```c
uint16_t fio_u16x8_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_or`
```c
uint16_t fio_u16x8_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_sub`
```c
uint16_t fio_u16x8_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_xor`
```c
uint16_t fio_u16x8_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reshuffle`
```c
void fio_u16x8_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x8_sub`
```c
void fio_u16x8_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_xor`
```c
void fio_u16x8_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_add`
```c
void fio_u16x16_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_and`
```c
void fio_u16x16_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_mul`
```c
void fio_u16x16_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_or`
```c
void fio_u16x16_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_add`
```c
uint16_t fio_u16x16_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_and`
```c
uint16_t fio_u16x16_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_max`
```c
uint16_t fio_u16x16_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_min`
```c
uint16_t fio_u16x16_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_mul`
```c
uint16_t fio_u16x16_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_or`
```c
uint16_t fio_u16x16_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_sub`
```c
uint16_t fio_u16x16_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_xor`
```c
uint16_t fio_u16x16_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reshuffle`
```c
void fio_u16x16_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x16_sub`
```c
void fio_u16x16_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_xor`
```c
void fio_u16x16_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_add`
```c
void fio_u16x32_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_and`
```c
void fio_u16x32_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_mul`
```c
void fio_u16x32_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_or`
```c
void fio_u16x32_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_add`
```c
uint16_t fio_u16x32_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_and`
```c
uint16_t fio_u16x32_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_max`
```c
uint16_t fio_u16x32_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_min`
```c
uint16_t fio_u16x32_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_mul`
```c
uint16_t fio_u16x32_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_or`
```c
uint16_t fio_u16x32_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_sub`
```c
uint16_t fio_u16x32_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_xor`
```c
uint16_t fio_u16x32_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reshuffle`
```c
void fio_u16x32_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x32_sub`
```c
void fio_u16x32_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_xor`
```c
void fio_u16x32_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_add`
```c
void fio_u16x64_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_and`
```c
void fio_u16x64_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_mul`
```c
void fio_u16x64_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_or`
```c
void fio_u16x64_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_add`
```c
uint16_t fio_u16x64_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_and`
```c
uint16_t fio_u16x64_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_max`
```c
uint16_t fio_u16x64_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_min`
```c
uint16_t fio_u16x64_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_mul`
```c
uint16_t fio_u16x64_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_or`
```c
uint16_t fio_u16x64_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_sub`
```c
uint16_t fio_u16x64_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_xor`
```c
uint16_t fio_u16x64_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reshuffle`
```c
void fio_u16x64_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x64_sub`
```c
void fio_u16x64_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_xor`
```c
void fio_u16x64_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_add`
```c
void fio_u16x128_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_and`
```c
void fio_u16x128_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_mul`
```c
void fio_u16x128_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_or`
```c
void fio_u16x128_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_add`
```c
uint16_t fio_u16x128_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_and`
```c
uint16_t fio_u16x128_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_max`
```c
uint16_t fio_u16x128_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_min`
```c
uint16_t fio_u16x128_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_mul`
```c
uint16_t fio_u16x128_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_or`
```c
uint16_t fio_u16x128_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_sub`
```c
uint16_t fio_u16x128_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_xor`
```c
uint16_t fio_u16x128_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reshuffle`
```c
void fio_u16x128_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x128_sub`
```c
void fio_u16x128_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_xor`
```c
void fio_u16x128_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_add`
```c
void fio_u32x2_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_and`
```c
void fio_u32x2_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_mul`
```c
void fio_u32x2_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_or`
```c
void fio_u32x2_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_add`
```c
uint32_t fio_u32x2_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_and`
```c
uint32_t fio_u32x2_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_max`
```c
uint32_t fio_u32x2_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_min`
```c
uint32_t fio_u32x2_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_mul`
```c
uint32_t fio_u32x2_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_or`
```c
uint32_t fio_u32x2_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_sub`
```c
uint32_t fio_u32x2_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_xor`
```c
uint32_t fio_u32x2_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reshuffle`
```c
void fio_u32x2_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x2_sub`
```c
void fio_u32x2_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_xor`
```c
void fio_u32x2_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_add`
```c
void fio_u32x4_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_and`
```c
void fio_u32x4_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_mul`
```c
void fio_u32x4_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_or`
```c
void fio_u32x4_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_add`
```c
uint32_t fio_u32x4_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_and`
```c
uint32_t fio_u32x4_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_max`
```c
uint32_t fio_u32x4_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_min`
```c
uint32_t fio_u32x4_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_mul`
```c
uint32_t fio_u32x4_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_or`
```c
uint32_t fio_u32x4_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_sub`
```c
uint32_t fio_u32x4_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_xor`
```c
uint32_t fio_u32x4_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reshuffle`
```c
void fio_u32x4_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x4_sub`
```c
void fio_u32x4_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_xor`
```c
void fio_u32x4_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_add`
```c
void fio_u32x8_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_and`
```c
void fio_u32x8_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_mul`
```c
void fio_u32x8_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_or`
```c
void fio_u32x8_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_add`
```c
uint32_t fio_u32x8_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_and`
```c
uint32_t fio_u32x8_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_max`
```c
uint32_t fio_u32x8_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_min`
```c
uint32_t fio_u32x8_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_mul`
```c
uint32_t fio_u32x8_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_or`
```c
uint32_t fio_u32x8_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_sub`
```c
uint32_t fio_u32x8_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_xor`
```c
uint32_t fio_u32x8_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reshuffle`
```c
void fio_u32x8_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x8_sub`
```c
void fio_u32x8_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_xor`
```c
void fio_u32x8_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_add`
```c
void fio_u32x16_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_and`
```c
void fio_u32x16_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_mul`
```c
void fio_u32x16_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_or`
```c
void fio_u32x16_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_add`
```c
uint32_t fio_u32x16_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_and`
```c
uint32_t fio_u32x16_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_max`
```c
uint32_t fio_u32x16_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_min`
```c
uint32_t fio_u32x16_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_mul`
```c
uint32_t fio_u32x16_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_or`
```c
uint32_t fio_u32x16_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_sub`
```c
uint32_t fio_u32x16_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_xor`
```c
uint32_t fio_u32x16_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reshuffle`
```c
void fio_u32x16_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x16_sub`
```c
void fio_u32x16_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_xor`
```c
void fio_u32x16_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_add`
```c
void fio_u32x32_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_and`
```c
void fio_u32x32_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_mul`
```c
void fio_u32x32_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_or`
```c
void fio_u32x32_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_add`
```c
uint32_t fio_u32x32_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_and`
```c
uint32_t fio_u32x32_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_max`
```c
uint32_t fio_u32x32_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_min`
```c
uint32_t fio_u32x32_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_mul`
```c
uint32_t fio_u32x32_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_or`
```c
uint32_t fio_u32x32_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_sub`
```c
uint32_t fio_u32x32_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_xor`
```c
uint32_t fio_u32x32_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reshuffle`
```c
void fio_u32x32_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x32_sub`
```c
void fio_u32x32_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_xor`
```c
void fio_u32x32_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_add`
```c
void fio_u32x64_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_and`
```c
void fio_u32x64_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_mul`
```c
void fio_u32x64_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_or`
```c
void fio_u32x64_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_add`
```c
uint32_t fio_u32x64_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_and`
```c
uint32_t fio_u32x64_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_max`
```c
uint32_t fio_u32x64_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_min`
```c
uint32_t fio_u32x64_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_mul`
```c
uint32_t fio_u32x64_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_or`
```c
uint32_t fio_u32x64_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_sub`
```c
uint32_t fio_u32x64_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_xor`
```c
uint32_t fio_u32x64_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reshuffle`
```c
void fio_u32x64_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x64_sub`
```c
void fio_u32x64_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_xor`
```c
void fio_u32x64_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_add`
```c
void fio_u64x2_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_and`
```c
void fio_u64x2_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_mul`
```c
void fio_u64x2_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_or`
```c
void fio_u64x2_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_add`
```c
uint64_t fio_u64x2_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_and`
```c
uint64_t fio_u64x2_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_max`
```c
uint64_t fio_u64x2_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_min`
```c
uint64_t fio_u64x2_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_mul`
```c
uint64_t fio_u64x2_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_or`
```c
uint64_t fio_u64x2_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_sub`
```c
uint64_t fio_u64x2_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_xor`
```c
uint64_t fio_u64x2_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reshuffle`
```c
void fio_u64x2_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x2_sub`
```c
void fio_u64x2_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_xor`
```c
void fio_u64x2_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_add`
```c
void fio_u64x4_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_and`
```c
void fio_u64x4_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_mul`
```c
void fio_u64x4_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_or`
```c
void fio_u64x4_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_add`
```c
uint64_t fio_u64x4_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_and`
```c
uint64_t fio_u64x4_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_max`
```c
uint64_t fio_u64x4_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_min`
```c
uint64_t fio_u64x4_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_mul`
```c
uint64_t fio_u64x4_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_or`
```c
uint64_t fio_u64x4_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_sub`
```c
uint64_t fio_u64x4_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_xor`
```c
uint64_t fio_u64x4_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reshuffle`
```c
void fio_u64x4_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x4_sub`
```c
void fio_u64x4_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_xor`
```c
void fio_u64x4_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_add`
```c
void fio_u64x8_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_and`
```c
void fio_u64x8_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_mul`
```c
void fio_u64x8_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_or`
```c
void fio_u64x8_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_add`
```c
uint64_t fio_u64x8_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_and`
```c
uint64_t fio_u64x8_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_max`
```c
uint64_t fio_u64x8_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_min`
```c
uint64_t fio_u64x8_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_mul`
```c
uint64_t fio_u64x8_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_or`
```c
uint64_t fio_u64x8_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_sub`
```c
uint64_t fio_u64x8_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_xor`
```c
uint64_t fio_u64x8_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reshuffle`
```c
void fio_u64x8_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x8_sub`
```c
void fio_u64x8_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_xor`
```c
void fio_u64x8_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_add`
```c
void fio_u64x16_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_and`
```c
void fio_u64x16_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_mul`
```c
void fio_u64x16_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_or`
```c
void fio_u64x16_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_add`
```c
uint64_t fio_u64x16_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_and`
```c
uint64_t fio_u64x16_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_max`
```c
uint64_t fio_u64x16_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_min`
```c
uint64_t fio_u64x16_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_mul`
```c
uint64_t fio_u64x16_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_or`
```c
uint64_t fio_u64x16_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_sub`
```c
uint64_t fio_u64x16_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_xor`
```c
uint64_t fio_u64x16_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reshuffle`
```c
void fio_u64x16_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x16_sub`
```c
void fio_u64x16_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_xor`
```c
void fio_u64x16_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_add`
```c
void fio_u64x32_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_and`
```c
void fio_u64x32_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_mul`
```c
void fio_u64x32_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_or`
```c
void fio_u64x32_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_add`
```c
uint64_t fio_u64x32_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_and`
```c
uint64_t fio_u64x32_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_max`
```c
uint64_t fio_u64x32_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_min`
```c
uint64_t fio_u64x32_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_mul`
```c
uint64_t fio_u64x32_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_or`
```c
uint64_t fio_u64x32_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_sub`
```c
uint64_t fio_u64x32_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_xor`
```c
uint64_t fio_u64x32_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reshuffle`
```c
void fio_u64x32_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x32_sub`
```c
void fio_u64x32_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_xor`
```c
void fio_u64x32_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_add`
```c
void fio_floatx2_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_mul`
```c
void fio_floatx2_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_add`
```c
float fio_floatx2_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_max`
```c
float fio_floatx2_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_min`
```c
float fio_floatx2_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_mul`
```c
float fio_floatx2_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx2_reshuffle`
```c
void fio_floatx2_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx4_add`
```c
void fio_floatx4_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx4_mul`
```c
void fio_floatx4_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_add`
```c
float fio_floatx4_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_max`
```c
float fio_floatx4_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_min`
```c
float fio_floatx4_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_mul`
```c
float fio_floatx4_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx4_reshuffle`
```c
void fio_floatx4_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx8_add`
```c
void fio_floatx8_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx8_mul`
```c
void fio_floatx8_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_add`
```c
float fio_floatx8_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_max`
```c
float fio_floatx8_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_min`
```c
float fio_floatx8_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_mul`
```c
float fio_floatx8_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx8_reshuffle`
```c
void fio_floatx8_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx16_add`
```c
void fio_floatx16_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx16_mul`
```c
void fio_floatx16_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_add`
```c
float fio_floatx16_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_max`
```c
float fio_floatx16_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_min`
```c
float fio_floatx16_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_mul`
```c
float fio_floatx16_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx16_reshuffle`
```c
void fio_floatx16_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx32_add`
```c
void fio_floatx32_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx32_mul`
```c
void fio_floatx32_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_add`
```c
float fio_floatx32_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_max`
```c
float fio_floatx32_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_min`
```c
float fio_floatx32_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_mul`
```c
float fio_floatx32_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx32_reshuffle`
```c
void fio_floatx32_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx64_add`
```c
void fio_floatx64_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx64_mul`
```c
void fio_floatx64_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_add`
```c
float fio_floatx64_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_max`
```c
float fio_floatx64_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_min`
```c
float fio_floatx64_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_mul`
```c
float fio_floatx64_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx64_reshuffle`
```c
void fio_floatx64_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx2_add`
```c
void fio_dblx2_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx2_mul`
```c
void fio_dblx2_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_add`
```c
double fio_dblx2_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_max`
```c
double fio_dblx2_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_min`
```c
double fio_dblx2_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_mul`
```c
double fio_dblx2_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx2_reshuffle`
```c
void fio_dblx2_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx4_add`
```c
void fio_dblx4_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx4_mul`
```c
void fio_dblx4_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_add`
```c
double fio_dblx4_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_max`
```c
double fio_dblx4_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_min`
```c
double fio_dblx4_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_mul`
```c
double fio_dblx4_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx4_reshuffle`
```c
void fio_dblx4_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx8_add`
```c
void fio_dblx8_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx8_mul`
```c
void fio_dblx8_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_add`
```c
double fio_dblx8_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_max`
```c
double fio_dblx8_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_min`
```c
double fio_dblx8_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_mul`
```c
double fio_dblx8_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx8_reshuffle`
```c
void fio_dblx8_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx16_add`
```c
void fio_dblx16_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx16_mul`
```c
void fio_dblx16_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_add`
```c
double fio_dblx16_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_max`
```c
double fio_dblx16_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_min`
```c
double fio_dblx16_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_mul`
```c
double fio_dblx16_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx16_reshuffle`
```c
void fio_dblx16_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx32_add`
```c
void fio_dblx32_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx32_mul`
```c
void fio_dblx32_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_add`
```c
double fio_dblx32_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_max`
```c
double fio_dblx32_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_min`
```c
double fio_dblx32_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_mul`
```c
double fio_dblx32_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx32_reshuffle`
```c
void fio_dblx32_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x4_reshuffle`
```c
#define fio_u8x4_reshuffle(v, ...) fio_u8x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x8_reshuffle`
```c
#define fio_u8x8_reshuffle(v, ...) fio_u8x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x16_reshuffle`
```c
#define fio_u8x16_reshuffle(v, ...) fio_u8x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x32_reshuffle`
```c
#define fio_u8x32_reshuffle(v, ...) fio_u8x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x64_reshuffle`
```c
#define fio_u8x64_reshuffle(v, ...) fio_u8x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x128_reshuffle`
```c
#define fio_u8x128_reshuffle(v, ...) fio_u8x128_reshuffle(v, (uint8_t[128]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x256_reshuffle`
```c
#define fio_u8x256_reshuffle(v, ...) fio_u8x256_reshuffle(v, (uint8_t[256]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x2_reshuffle`
```c
#define fio_u16x2_reshuffle(v, ...) fio_u16x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x4_reshuffle`
```c
#define fio_u16x4_reshuffle(v, ...) fio_u16x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x8_reshuffle`
```c
#define fio_u16x8_reshuffle(v, ...) fio_u16x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x16_reshuffle`
```c
#define fio_u16x16_reshuffle(v, ...) fio_u16x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x32_reshuffle`
```c
#define fio_u16x32_reshuffle(v, ...) fio_u16x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x64_reshuffle`
```c
#define fio_u16x64_reshuffle(v, ...) fio_u16x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x128_reshuffle`
```c
#define fio_u16x128_reshuffle(v,...) fio_u16x128_reshuffle(v, (uint8_t[128]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x2_reshuffle`
```c
#define fio_u32x2_reshuffle(v, ...) fio_u32x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x4_reshuffle`
```c
#define fio_u32x4_reshuffle(v, ...) fio_u32x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x8_reshuffle`
```c
#define fio_u32x8_reshuffle(v, ...) fio_u32x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x16_reshuffle`
```c
#define fio_u32x16_reshuffle(v, ...) fio_u32x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x32_reshuffle`
```c
#define fio_u32x32_reshuffle(v, ...) fio_u32x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x64_reshuffle`
```c
#define fio_u32x64_reshuffle(v, ...) fio_u32x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x2_reshuffle`
```c
#define fio_u64x2_reshuffle(v, ...) fio_u64x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x4_reshuffle`
```c
#define fio_u64x4_reshuffle(v, ...) fio_u64x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x8_reshuffle`
```c
#define fio_u64x8_reshuffle(v, ...) fio_u64x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x16_reshuffle`
```c
#define fio_u64x16_reshuffle(v, ...) fio_u64x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x32_reshuffle`
```c
#define fio_u64x32_reshuffle(v, ...) fio_u64x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx2_reshuffle`
```c
#define fio_floatx2_reshuffle(v, ...) fio_floatx2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx4_reshuffle`
```c
#define fio_floatx4_reshuffle(v, ...) fio_floatx4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx8_reshuffle`
```c
#define fio_floatx8_reshuffle(v, ...) fio_floatx8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx16_reshuffle`
```c
#define fio_floatx16_reshuffle(v, ...) fio_floatx16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx32_reshuffle`
```c
#define fio_floatx32_reshuffle(v, ...) fio_floatx32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx64_reshuffle`
```c
#define fio_floatx64_reshuffle(v, ...) fio_floatx64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx2_reshuffle`
```c
#define fio_dblx2_reshuffle(v, ...) fio_dblx2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx4_reshuffle`
```c
#define fio_dblx4_reshuffle(v, ...) fio_dblx4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx8_reshuffle`
```c
#define fio_dblx8_reshuffle(v, ...) fio_dblx8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx16_reshuffle`
```c
#define fio_dblx16_reshuffle(v, ...) fio_dblx16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx32_reshuffle`
```c
#define fio_dblx32_reshuffle(v, ...) fio_dblx32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ct_true`
```c
inline FIO_CONST uintmax_t fio_ct_true(uintmax_t cond)
```
Returns 1 if the expression is true (input isn't zero).
_Symbol type:_ `function`
#### `fio_ct_false`
```c
inline FIO_CONST uintmax_t fio_ct_false(uintmax_t cond)
```
Returns 1 if the expression is false (input is zero).
_Symbol type:_ `function`
#### `fio_ct_if_bool`
```c
inline FIO_CONST uintmax_t fio_ct_if_bool(uintmax_t cond, uintmax_t a, uintmax_t b)
```
Returns `a` if `cond` is boolean and true, returns b otherwise.
_Symbol type:_ `function`
#### `fio_ct_if`
```c
inline FIO_CONST uintmax_t fio_ct_if(uintmax_t cond, uintmax_t a, uintmax_t b)
```
Returns `a` if `cond` isn't zero (uses fio_ct_true), returns b otherwise.
_Symbol type:_ `function`
#### `fio_ct_max`
```c
inline FIO_CONST intmax_t fio_ct_max(intmax_t a_, intmax_t b_)
```
Returns `a` if a >= `b`.
_Symbol type:_ `function`
#### `fio_ct_min`
```c
inline FIO_CONST intmax_t fio_ct_min(intmax_t a_, intmax_t b_)
```
Returns `a` if a >= `b`.
_Symbol type:_ `function`
#### `fio_ct_abs`
```c
inline FIO_CONST uintmax_t fio_ct_abs(intmax_t i_)
```
Returns absolute value.
_Symbol type:_ `function`
#### `fio_ct_tolower`
```c
inline char fio_ct_tolower(char c)
```
_Symbol type:_ `function`
#### `fio_ct_mux32`
```c
inline FIO_CONST uint32_t fio_ct_mux32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "choose": for each bit, if x is set, return y's bit, else z's bit.
Formula: (x & y) ^ (~x & z) -or equivalently- z ^ (x & (y ^ z))
Used in: SHA-1 (Ch), SHA-256 (Ch), SHA-512 (Ch), AES, etc.
_Symbol type:_ `function`
#### `fio_ct_mux64`
```c
inline FIO_CONST uint64_t fio_ct_mux64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of bitwise choose/mux.
_Symbol type:_ `function`
#### `fio_ct_maj32`
```c
inline FIO_CONST uint32_t fio_ct_maj32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "majority": for each bit position, return 1 if 2+ inputs have 1.
Formula: (x & y) ^ (x & z) ^ (y & z) -or- (x & y) | (z & (x | y))
Used in: SHA-1 (Maj), SHA-256 (Maj), SHA-512 (Maj), etc.
_Symbol type:_ `function`
#### `fio_ct_maj64`
```c
inline FIO_CONST uint64_t fio_ct_maj64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of bitwise majority.
_Symbol type:_ `function`
#### `fio_ct_xor3_32`
```c
inline FIO_CONST uint32_t fio_ct_xor3_32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "parity": XOR of all three inputs (1 if odd number of 1s).
Formula: x ^ y ^ z
Used in: SHA-1 (Parity function for rounds 20-39 and 60-79)
_Symbol type:_ `function`
#### `fio_ct_xor3_64`
```c
inline FIO_CONST uint64_t fio_ct_xor3_64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of 3-way XOR.
_Symbol type:_ `function`
#### `fio_ct_is_eq`
```c
FIO_SFUNC _Bool fio_ct_is_eq(const void *a_, const void *b_, size_t bytes)
```
A timing attack resistant memory comparison function.
_Symbol type:_ `function`
#### `fio_secure_zero`
```c
inline void fio_secure_zero(void *a_, size_t bytes)
```
A timing attack resistant memory zeroing function.
_Symbol type:_ `function`
#### `fio_lrot8`
```c
#define fio_lrot8(i, bits) __builtin_rotateleft8(i, bits)
```
8Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot16`
```c
#define fio_lrot16(i, bits) __builtin_rotateleft16(i, bits)
```
16Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot32`
```c
#define fio_lrot32(i, bits) __builtin_rotateleft32(i, bits)
```
32Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot64`
```c
#define fio_lrot64(i, bits) __builtin_rotateleft64(i, bits)
```
64Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot8`
```c
#define fio_rrot8(i, bits) __builtin_rotateright8(i, bits)
```
8Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot8`
```c
inline FIO_CONST uint8_t fio_rrot8(uint8_t i, uint8_t bits)
```
8Bit right rotation, inlined.
_Symbol type:_ `function`
#### `fio_rrot16`
```c
#define fio_rrot16(i, bits) __builtin_rotateright16(i, bits)
```
16Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot32`
```c
#define fio_rrot32(i, bits) __builtin_rotateright32(i, bits)
```
32Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot64`
```c
#define fio_rrot64(i, bits) __builtin_rotateright64(i, bits)
```
64Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot128`
```c
#define fio_lrot128(i, bits) __builtin_rotateleft128(i, bits)
```
128Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot128`
```c
#define fio_rrot128(i, bits) __builtin_rotateright128(i, bits)
```
128Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot128`
```c
inline FIO_CONST __uint128_t fio_lrot128(__uint128_t i, uint8_t bits)
```
128Bit left rotation, inlined.
_Symbol type:_ `function`
#### `fio_rrot128`
```c
inline FIO_CONST __uint128_t fio_rrot128(__uint128_t i, uint8_t bits)
```
128Bit right rotation, inlined.
_Symbol type:_ `function`
#### `fio_frot16`
```c
#define fio_frot16 fio_rrot16
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_frot32`
```c
#define fio_frot32 fio_rrot32
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_frot64`
```c
#define fio_frot64 fio_rrot64
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_xor_rrot3_32`
```c
inline FIO_CONST uint32_t fio_xor_rrot3_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c)
```
XOR of three right rotations: ROTR(x,a) ^ ROTR(x,b) ^ ROTR(x,c)
Common in SHA-256/SHA-512 Sigma functions.
_Symbol type:_ `function`
#### `fio_xor_rrot3_64`
```c
inline FIO_CONST uint64_t fio_xor_rrot3_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c)
```
64-bit version of triple rotation XOR.
_Symbol type:_ `function`
#### `fio_xor_rrot2_shr_32`
```c
inline FIO_CONST uint32_t fio_xor_rrot2_shr_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c)
```
XOR of two right rotations and a right shift: ROTR(x,a) ^ ROTR(x,b) ^
SHR(x,c) Common in SHA-256/SHA-512 sigma (lowercase) functions for message
schedule.
_Symbol type:_ `function`
#### `fio_xor_rrot2_shr_64`
```c
inline FIO_CONST uint64_t fio_xor_rrot2_shr_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c)
```
64-bit version of double rotation + shift XOR.
_Symbol type:_ `function`
#### `fio_xmask`
```c
inline void fio_xmask(void *buf_, size_t len, uint64_t mask)
```
Masks data using a persistent 64 bit mask.
When the buffer's memory is aligned, the function may perform significantly
better.
_Symbol type:_ `function`
#### `fio_xmask_cpy`
```c
inline void fio_xmask_cpy(char *restrict dest, const char *src, size_t len, uint64_t mask)
```
Masks data using a persistent 64 bit mask.
When the buffer's memory is aligned, the function may perform significantly
better.
_Symbol type:_ `function`
#### `fio_popcount`
```c
#define fio_popcount(n) __builtin_popcountll(n)
```
performs a `popcount` operation to count the set bits.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_hemming_dist`
```c
#define fio_hemming_dist(n1, n2) fio_popcount(((uint64_t)(n1) ^ (uint64_t)(n2)))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lsb_index_unsafe`
```c
FIO_SFUNC FIO_CONST size_t fio_lsb_index_unsafe(uint64_t i)
```
Returns the index of the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_msb_index_unsafe`
```c
FIO_SFUNC FIO_CONST size_t fio_msb_index_unsafe(uint64_t i)
```
Returns the index of the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_has_zero_byte32`
```c
inline FIO_CONST uint32_t fio_has_zero_byte32(uint32_t row)
```
Detects a byte where no bits are set (0) within a 4 byte vector.
The zero byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_byte32`
```c
inline FIO_CONST uint32_t fio_has_byte32(uint32_t row, uint8_t byte)
```
Detects if `byte` exists within a 4 byte vector.
The requested byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_full_byte32`
```c
inline FIO_CONST uint32_t fio_has_full_byte32(uint32_t row)
```
Detects a byte where all the bits are set (255) within a 4 byte vector.
The full byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_zero_byte64`
```c
inline FIO_CONST uint64_t fio_has_zero_byte64(uint64_t row)
```
Detects a byte where no bits are set (0) within an 8 byte vector.
The zero byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_zero_byte_alt64`
```c
inline FIO_CONST uint64_t fio_has_zero_byte_alt64(uint64_t row)
```
Detects a byte where no bits are set (0) within an 8 byte vector.
This variation should NOT be used to build a bitmap, but May be used to
detect the first occurrence.
_Symbol type:_ `function`
#### `fio_has_byte64`
```c
inline FIO_CONST uint64_t fio_has_byte64(uint64_t row, uint8_t byte)
```
Detects if `byte` exists within an 8 byte vector.
The requested byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_full_byte64`
```c
inline FIO_CONST uint64_t fio_has_full_byte64(uint64_t row)
```
Detects a byte where all the bits are set (255) within an 8 byte vector.
The full byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_byte2bitmap`
```c
inline FIO_CONST uint64_t fio_has_byte2bitmap(uint64_t result)
```
Converts a `fio_has_byteX` result to a bitmap.
_Symbol type:_ `function`
#### `fio_bits_lsb`
```c
inline FIO_CONST uint64_t fio_bits_lsb(uint64_t i)
```
Isolates the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_bits_msb`
```c
inline FIO_CONST uint64_t fio_bits_msb(uint64_t i)
```
Isolates the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_bits_msb_index`
```c
inline FIO_CONST size_t fio_bits_msb_index(uint64_t i)
```
Returns the index of the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_bits_lsb_index`
```c
inline FIO_CONST size_t fio_bits_lsb_index(uint64_t i)
```
Returns the index of the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_bit_get`
```c
inline uint8_t fio_bit_get(void *map, size_t bit)
```
Gets the state of a bit in a bitmap.
_Symbol type:_ `function`
#### `fio_bit_set`
```c
inline void fio_bit_set(void *map, size_t bit)
```
Sets the a bit in a bitmap (sets to 1).
_Symbol type:_ `function`
#### `fio_bit_unset`
```c
inline void fio_bit_unset(void *map, size_t bit)
```
Unsets the a bit in a bitmap (sets to 0).
_Symbol type:_ `function`
#### `fio_bit_flip`
```c
inline void fio_bit_flip(void *map, size_t bit)
```
Flips the a bit in a bitmap (sets to 0 if 1, sets to 1 if 0).
_Symbol type:_ `function`
#### `fio_math_mod_mul64`
```c
inline uint64_t fio_math_mod_mul64(uint64_t a, uint64_t b, uint64_t mod)
```
Perform modular multiplication for numbers with up to 64 bits.
_Symbol type:_ `function`
#### `fio_math_mod_expo64`
```c
inline uint64_t fio_math_mod_expo64(uint64_t base, uint64_t exp, uint64_t mod)
```
Perform modular exponentiation
_Symbol type:_ `function`
#### `fio_math_is_uprime`
```c
FIO_SFUNC bool fio_math_is_uprime(uint64_t n)
```
Tests if an unsigned 64 bit number is (probably) a prime.
For numbers up to 1023 this is deterministic.
_Symbol type:_ `function`
#### `fio_math_is_iprime`
```c
inline bool fio_math_is_iprime(int64_t n)
```
Tests if the absolute value of a signed 64 bit number is (probably) a prime.
For numbers up to 1023 this is deterministic.
_Symbol type:_ `function`
#### `fio_math_addc64`
```c
FIO_MIFN uint64_t fio_math_addc64(uint64_t a, uint64_t b, uint64_t carry_in, uint64_t *carry_out)
```
Add with carry.
_Symbol type:_ `function`
#### `fio_math_add`
```c
FIO_MIFN bool fio_math_add(uint64_t *dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision ADD for `len` 64 bit words a + b. Returns the carry.
_Symbol type:_ `function`
#### `fio_math_addc128`
```c
FIO_MIFN __uint128_t fio_math_addc128(const __uint128_t a, const __uint128_t b, bool carry_in, bool *carry_out)
```
Multi-precision ADD for `bits` long a + b. Returns the carry.
_Symbol type:_ `function`
#### `fio_math_add2`
```c
FIO_MIFN bool fio_math_add2(__uint128_t *dest, const __uint128_t *a, const __uint128_t *b, const size_t len)
```
_Symbol type:_ `function`
#### `fio_math_subc64`
```c
FIO_MIFN uint64_t fio_math_subc64(uint64_t a, uint64_t b, uint64_t borrow_in, uint64_t *borrow_out)
```
Subtract with borrow.
_Symbol type:_ `function`
#### `fio_math_sub`
```c
FIO_MIFN uint64_t fio_math_sub(uint64_t *dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision SUB for `len` 64 bit words a + b. Returns the borrow.
_Symbol type:_ `function`
#### `fio_math_mulc64`
```c
FIO_MIFN uint64_t fio_math_mulc64(uint64_t a, uint64_t b, uint64_t *carry_out)
```
Multiply with carry out.
_Symbol type:_ `function`
#### `fio_math_mul64_fold`
```c
FIO_MIFN uint64_t fio_math_mul64_fold(uint64_t a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_math_mul`
```c
inline void fio_math_mul(uint64_t *restrict dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision MUL for `len` 64 bit words.
`dest` must be `len * 2` long to hold the result.
`a` and `b` must be of equal `len`.
_Symbol type:_ `function`
#### `fio_u128_init8`
```c
#define fio_u128_init8(...) ((fio_u128){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init16`
```c
#define fio_u128_init16(...) ((fio_u128){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init32`
```c
#define fio_u128_init32(...) ((fio_u128){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init64`
```c
#define fio_u128_init64(...) ((fio_u128){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init8`
```c
#define fio_u256_init8(...) ((fio_u256){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init16`
```c
#define fio_u256_init16(...) ((fio_u256){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init32`
```c
#define fio_u256_init32(...) ((fio_u256){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init64`
```c
#define fio_u256_init64(...) ((fio_u256){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init8`
```c
#define fio_u512_init8(...) ((fio_u512){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init16`
```c
#define fio_u512_init16(...) ((fio_u512){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init32`
```c
#define fio_u512_init32(...) ((fio_u512){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init64`
```c
#define fio_u512_init64(...) ((fio_u512){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init8`
```c
#define fio_u1024_init8(...) ((fio_u1024){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init16`
```c
#define fio_u1024_init16(...) ((fio_u1024){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init32`
```c
#define fio_u1024_init32(...) ((fio_u1024){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init64`
```c
#define fio_u1024_init64(...) ((fio_u1024){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init8`
```c
#define fio_u2048_init8(...) ((fio_u2048){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init16`
```c
#define fio_u2048_init16(...) ((fio_u2048){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init32`
```c
#define fio_u2048_init32(...) ((fio_u2048){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init64`
```c
#define fio_u2048_init64(...) ((fio_u2048){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init8`
```c
#define fio_u4096_init8(...) ((fio_u4096){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init16`
```c
#define fio_u4096_init16(...) ((fio_u4096){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init32`
```c
#define fio_u4096_init32(...) ((fio_u4096){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init64`
```c
#define fio_u4096_init64(...) ((fio_u4096){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_bswap16`
```c
fio_u128 fio_u128_bswap16(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_bswap32`
```c
fio_u128 fio_u128_bswap32(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_bswap64`
```c
fio_u128 fio_u128_bswap64(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_load`
```c
fio_u128 fio_u128_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u128_load_be16`
```c
fio_u128 fio_u128_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_be32`
```c
fio_u128 fio_u128_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_be64`
```c
fio_u128 fio_u128_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le16`
```c
fio_u128 fio_u128_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le32`
```c
fio_u128 fio_u128_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le64`
```c
fio_u128 fio_u128_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_store`
```c
void fio_u128_store(void *buf, const fio_u128 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u256_bswap16`
```c
fio_u256 fio_u256_bswap16(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_bswap32`
```c
fio_u256 fio_u256_bswap32(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_bswap64`
```c
fio_u256 fio_u256_bswap64(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_load`
```c
fio_u256 fio_u256_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u256_load_be16`
```c
fio_u256 fio_u256_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_be32`
```c
fio_u256 fio_u256_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_be64`
```c
fio_u256 fio_u256_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le16`
```c
fio_u256 fio_u256_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le32`
```c
fio_u256 fio_u256_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le64`
```c
fio_u256 fio_u256_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_store`
```c
void fio_u256_store(void *buf, const fio_u256 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u512_bswap16`
```c
fio_u512 fio_u512_bswap16(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_bswap32`
```c
fio_u512 fio_u512_bswap32(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_bswap64`
```c
fio_u512 fio_u512_bswap64(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_load`
```c
fio_u512 fio_u512_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u512_load_be16`
```c
fio_u512 fio_u512_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_be32`
```c
fio_u512 fio_u512_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_be64`
```c
fio_u512 fio_u512_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le16`
```c
fio_u512 fio_u512_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le32`
```c
fio_u512 fio_u512_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le64`
```c
fio_u512 fio_u512_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_store`
```c
void fio_u512_store(void *buf, const fio_u512 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u1024_bswap16`
```c
fio_u1024 fio_u1024_bswap16(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_bswap32`
```c
fio_u1024 fio_u1024_bswap32(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_bswap64`
```c
fio_u1024 fio_u1024_bswap64(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_load`
```c
fio_u1024 fio_u1024_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u1024_load_be16`
```c
fio_u1024 fio_u1024_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_be32`
```c
fio_u1024 fio_u1024_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_be64`
```c
fio_u1024 fio_u1024_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le16`
```c
fio_u1024 fio_u1024_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le32`
```c
fio_u1024 fio_u1024_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le64`
```c
fio_u1024 fio_u1024_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_store`
```c
void fio_u1024_store(void *buf, const fio_u1024 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u2048_bswap16`
```c
fio_u2048 fio_u2048_bswap16(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_bswap32`
```c
fio_u2048 fio_u2048_bswap32(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_bswap64`
```c
fio_u2048 fio_u2048_bswap64(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_load`
```c
fio_u2048 fio_u2048_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u2048_load_be16`
```c
fio_u2048 fio_u2048_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_be32`
```c
fio_u2048 fio_u2048_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_be64`
```c
fio_u2048 fio_u2048_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le16`
```c
fio_u2048 fio_u2048_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le32`
```c
fio_u2048 fio_u2048_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le64`
```c
fio_u2048 fio_u2048_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_store`
```c
void fio_u2048_store(void *buf, const fio_u2048 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u4096_bswap16`
```c
fio_u4096 fio_u4096_bswap16(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_bswap32`
```c
fio_u4096 fio_u4096_bswap32(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_bswap64`
```c
fio_u4096 fio_u4096_bswap64(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_load`
```c
fio_u4096 fio_u4096_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u4096_load_be16`
```c
fio_u4096 fio_u4096_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_be32`
```c
fio_u4096 fio_u4096_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_be64`
```c
fio_u4096 fio_u4096_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le16`
```c
fio_u4096 fio_u4096_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le32`
```c
fio_u4096 fio_u4096_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le64`
```c
fio_u4096 fio_u4096_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_store`
```c
void fio_u4096_store(void *buf, const fio_u4096 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u128_3xor`
```c
void fio_u128_3xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_3xor32`
```c
void fio_u128_3xor32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_3xor64`
```c
void fio_u128_3xor64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_add16`
```c
void fio_u128_add16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add32`
```c
void fio_u128_add32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add64`
```c
void fio_u128_add64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add8`
```c
void fio_u128_add8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and`
```c
void fio_u128_and(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and16`
```c
void fio_u128_and16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and32`
```c
void fio_u128_and32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and64`
```c
void fio_u128_and64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and8`
```c
void fio_u128_and8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd16`
```c
void fio_u128_cadd16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd32`
```c
void fio_u128_cadd32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd64`
```c
void fio_u128_cadd64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd8`
```c
void fio_u128_cadd8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand16`
```c
void fio_u128_cand16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand32`
```c
void fio_u128_cand32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand64`
```c
void fio_u128_cand64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand8`
```c
void fio_u128_cand8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_clrot16`
```c
void fio_u128_clrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot32`
```c
void fio_u128_clrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot64`
```c
void fio_u128_clrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot8`
```c
void fio_u128_clrot8(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_cmul16`
```c
void fio_u128_cmul16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul32`
```c
void fio_u128_cmul32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul64`
```c
void fio_u128_cmul64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul8`
```c
void fio_u128_cmul8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor16`
```c
void fio_u128_cor16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor32`
```c
void fio_u128_cor32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor64`
```c
void fio_u128_cor64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor8`
```c
void fio_u128_cor8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_crrot16`
```c
void fio_u128_crrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot32`
```c
void fio_u128_crrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot64`
```c
void fio_u128_crrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot8`
```c
void fio_u128_crrot8(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_csub16`
```c
void fio_u128_csub16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub32`
```c
void fio_u128_csub32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub64`
```c
void fio_u128_csub64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub8`
```c
void fio_u128_csub8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_ct_swap_if`
```c
void fio_u128_ct_swap_if(bool cond, fio_u128 *restrict a, fio_u128 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor16`
```c
void fio_u128_cxor16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor32`
```c
void fio_u128_cxor32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor64`
```c
void fio_u128_cxor64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor8`
```c
void fio_u128_cxor8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_inv`
```c
void fio_u128_inv(fio_u128 *target, const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_is_eq`
```c
bool fio_u128_is_eq(const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_lrot16`
```c
void fio_u128_lrot16(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot32`
```c
void fio_u128_lrot32(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot64`
```c
void fio_u128_lrot64(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot8`
```c
void fio_u128_lrot8(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_maj`
```c
void fio_u128_maj(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_maj32`
```c
void fio_u128_maj32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_maj64`
```c
void fio_u128_maj64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mul16`
```c
void fio_u128_mul16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul32`
```c
void fio_u128_mul32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul64`
```c
void fio_u128_mul64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul8`
```c
void fio_u128_mul8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mux`
```c
void fio_u128_mux(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mux32`
```c
void fio_u128_mux32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mux64`
```c
void fio_u128_mux64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_or`
```c
void fio_u128_or(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or16`
```c
void fio_u128_or16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or32`
```c
void fio_u128_or32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or64`
```c
void fio_u128_or64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or8`
```c
void fio_u128_or8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add16`
```c
uint16_t fio_u128_reduce_add16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add32`
```c
uint32_t fio_u128_reduce_add32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add64`
```c
uint64_t fio_u128_reduce_add64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add8`
```c
uint8_t fio_u128_reduce_add8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and16`
```c
uint16_t fio_u128_reduce_and16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and32`
```c
uint32_t fio_u128_reduce_and32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and64`
```c
uint64_t fio_u128_reduce_and64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and8`
```c
uint8_t fio_u128_reduce_and8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul16`
```c
uint16_t fio_u128_reduce_mul16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul32`
```c
uint32_t fio_u128_reduce_mul32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul64`
```c
uint64_t fio_u128_reduce_mul64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul8`
```c
uint8_t fio_u128_reduce_mul8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or16`
```c
uint16_t fio_u128_reduce_or16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or32`
```c
uint32_t fio_u128_reduce_or32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or64`
```c
uint64_t fio_u128_reduce_or64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or8`
```c
uint8_t fio_u128_reduce_or8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub16`
```c
uint16_t fio_u128_reduce_sub16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub32`
```c
uint32_t fio_u128_reduce_sub32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub64`
```c
uint64_t fio_u128_reduce_sub64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub8`
```c
uint8_t fio_u128_reduce_sub8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor16`
```c
uint16_t fio_u128_reduce_xor16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor32`
```c
uint32_t fio_u128_reduce_xor32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor64`
```c
uint64_t fio_u128_reduce_xor64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor8`
```c
uint8_t fio_u128_reduce_xor8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_rrot16`
```c
void fio_u128_rrot16(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot32`
```c
void fio_u128_rrot32(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot64`
```c
void fio_u128_rrot64(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot8`
```c
void fio_u128_rrot8(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_sub16`
```c
void fio_u128_sub16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub32`
```c
void fio_u128_sub32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub64`
```c
void fio_u128_sub64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub8`
```c
void fio_u128_sub8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor`
```c
void fio_u128_xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor16`
```c
void fio_u128_xor16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor32`
```c
void fio_u128_xor32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor64`
```c
void fio_u128_xor64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor8`
```c
void fio_u128_xor8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u256_3xor`
```c
void fio_u256_3xor(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_3xor32`
```c
void fio_u256_3xor32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_3xor64`
```c
void fio_u256_3xor64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_add16`
```c
void fio_u256_add16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add32`
```c
void fio_u256_add32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add64`
```c
void fio_u256_add64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add8`
```c
void fio_u256_add8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and`
```c
void fio_u256_and(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and16`
```c
void fio_u256_and16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and32`
```c
void fio_u256_and32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and64`
```c
void fio_u256_and64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and8`
```c
void fio_u256_and8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd16`
```c
void fio_u256_cadd16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd32`
```c
void fio_u256_cadd32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd64`
```c
void fio_u256_cadd64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd8`
```c
void fio_u256_cadd8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand16`
```c
void fio_u256_cand16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand32`
```c
void fio_u256_cand32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand64`
```c
void fio_u256_cand64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand8`
```c
void fio_u256_cand8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_clrot16`
```c
void fio_u256_clrot16(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot32`
```c
void fio_u256_clrot32(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot64`
```c
void fio_u256_clrot64(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot8`
```c
void fio_u256_clrot8(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_cmul16`
```c
void fio_u256_cmul16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul32`
```c
void fio_u256_cmul32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul64`
```c
void fio_u256_cmul64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul8`
```c
void fio_u256_cmul8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor16`
```c
void fio_u256_cor16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor32`
```c
void fio_u256_cor32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor64`
```c
void fio_u256_cor64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor8`
```c
void fio_u256_cor8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_crrot16`
```c
void fio_u256_crrot16(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot32`
```c
void fio_u256_crrot32(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot64`
```c
void fio_u256_crrot64(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot8`
```c
void fio_u256_crrot8(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_csub16`
```c
void fio_u256_csub16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub32`
```c
void fio_u256_csub32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub64`
```c
void fio_u256_csub64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub8`
```c
void fio_u256_csub8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_ct_swap_if`
```c
void fio_u256_ct_swap_if(bool cond, fio_u256 *restrict a, fio_u256 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor16`
```c
void fio_u256_cxor16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor32`
```c
void fio_u256_cxor32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor64`
```c
void fio_u256_cxor64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor8`
```c
void fio_u256_cxor8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_inv`
```c
void fio_u256_inv(fio_u256 *target, const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_is_eq`
```c
bool fio_u256_is_eq(const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_lrot16`
```c
void fio_u256_lrot16(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot32`
```c
void fio_u256_lrot32(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot64`
```c
void fio_u256_lrot64(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot8`
```c
void fio_u256_lrot8(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_maj`
```c
void fio_u256_maj(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_maj32`
```c
void fio_u256_maj32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_maj64`
```c
void fio_u256_maj64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mul16`
```c
void fio_u256_mul16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul32`
```c
void fio_u256_mul32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul64`
```c
void fio_u256_mul64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul8`
```c
void fio_u256_mul8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mux`
```c
void fio_u256_mux(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mux32`
```c
void fio_u256_mux32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mux64`
```c
void fio_u256_mux64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_or`
```c
void fio_u256_or(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or16`
```c
void fio_u256_or16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or32`
```c
void fio_u256_or32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or64`
```c
void fio_u256_or64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or8`
```c
void fio_u256_or8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add16`
```c
uint16_t fio_u256_reduce_add16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add32`
```c
uint32_t fio_u256_reduce_add32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add64`
```c
uint64_t fio_u256_reduce_add64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add8`
```c
uint8_t fio_u256_reduce_add8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and16`
```c
uint16_t fio_u256_reduce_and16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and32`
```c
uint32_t fio_u256_reduce_and32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and64`
```c
uint64_t fio_u256_reduce_and64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and8`
```c
uint8_t fio_u256_reduce_and8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul16`
```c
uint16_t fio_u256_reduce_mul16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul32`
```c
uint32_t fio_u256_reduce_mul32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul64`
```c
uint64_t fio_u256_reduce_mul64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul8`
```c
uint8_t fio_u256_reduce_mul8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or16`
```c
uint16_t fio_u256_reduce_or16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or32`
```c
uint32_t fio_u256_reduce_or32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or64`
```c
uint64_t fio_u256_reduce_or64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or8`
```c
uint8_t fio_u256_reduce_or8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub16`
```c
uint16_t fio_u256_reduce_sub16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub32`
```c
uint32_t fio_u256_reduce_sub32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub64`
```c
uint64_t fio_u256_reduce_sub64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub8`
```c
uint8_t fio_u256_reduce_sub8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor16`
```c
uint16_t fio_u256_reduce_xor16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor32`
```c
uint32_t fio_u256_reduce_xor32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor64`
```c
uint64_t fio_u256_reduce_xor64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor8`
```c
uint8_t fio_u256_reduce_xor8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_rrot16`
```c
void fio_u256_rrot16(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot32`
```c
void fio_u256_rrot32(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot64`
```c
void fio_u256_rrot64(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot8`
```c
void fio_u256_rrot8(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_sub16`
```c
void fio_u256_sub16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub32`
```c
void fio_u256_sub32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub64`
```c
void fio_u256_sub64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub8`
```c
void fio_u256_sub8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor`
```c
void fio_u256_xor(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor16`
```c
void fio_u256_xor16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor32`
```c
void fio_u256_xor32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor64`
```c
void fio_u256_xor64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor8`
```c
void fio_u256_xor8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u512_3xor`
```c
void fio_u512_3xor(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_3xor32`
```c
void fio_u512_3xor32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_3xor64`
```c
void fio_u512_3xor64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_add16`
```c
void fio_u512_add16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add32`
```c
void fio_u512_add32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add64`
```c
void fio_u512_add64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add8`
```c
void fio_u512_add8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and`
```c
void fio_u512_and(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and16`
```c
void fio_u512_and16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and32`
```c
void fio_u512_and32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and64`
```c
void fio_u512_and64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and8`
```c
void fio_u512_and8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd16`
```c
void fio_u512_cadd16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd32`
```c
void fio_u512_cadd32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd64`
```c
void fio_u512_cadd64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd8`
```c
void fio_u512_cadd8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand16`
```c
void fio_u512_cand16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand32`
```c
void fio_u512_cand32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand64`
```c
void fio_u512_cand64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand8`
```c
void fio_u512_cand8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_clrot16`
```c
void fio_u512_clrot16(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot32`
```c
void fio_u512_clrot32(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot64`
```c
void fio_u512_clrot64(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot8`
```c
void fio_u512_clrot8(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_cmul16`
```c
void fio_u512_cmul16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul32`
```c
void fio_u512_cmul32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul64`
```c
void fio_u512_cmul64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul8`
```c
void fio_u512_cmul8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor16`
```c
void fio_u512_cor16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor32`
```c
void fio_u512_cor32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor64`
```c
void fio_u512_cor64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor8`
```c
void fio_u512_cor8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_crrot16`
```c
void fio_u512_crrot16(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot32`
```c
void fio_u512_crrot32(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot64`
```c
void fio_u512_crrot64(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot8`
```c
void fio_u512_crrot8(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_csub16`
```c
void fio_u512_csub16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub32`
```c
void fio_u512_csub32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub64`
```c
void fio_u512_csub64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub8`
```c
void fio_u512_csub8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_ct_swap_if`
```c
void fio_u512_ct_swap_if(bool cond, fio_u512 *restrict a, fio_u512 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor16`
```c
void fio_u512_cxor16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor32`
```c
void fio_u512_cxor32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor64`
```c
void fio_u512_cxor64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor8`
```c
void fio_u512_cxor8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_inv`
```c
void fio_u512_inv(fio_u512 *target, const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_is_eq`
```c
bool fio_u512_is_eq(const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_lrot16`
```c
void fio_u512_lrot16(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot32`
```c
void fio_u512_lrot32(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot64`
```c
void fio_u512_lrot64(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot8`
```c
void fio_u512_lrot8(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_maj`
```c
void fio_u512_maj(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_maj32`
```c
void fio_u512_maj32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_maj64`
```c
void fio_u512_maj64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mul16`
```c
void fio_u512_mul16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul32`
```c
void fio_u512_mul32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul64`
```c
void fio_u512_mul64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul8`
```c
void fio_u512_mul8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mux`
```c
void fio_u512_mux(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mux32`
```c
void fio_u512_mux32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mux64`
```c
void fio_u512_mux64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_or`
```c
void fio_u512_or(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or16`
```c
void fio_u512_or16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or32`
```c
void fio_u512_or32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or64`
```c
void fio_u512_or64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or8`
```c
void fio_u512_or8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add16`
```c
uint16_t fio_u512_reduce_add16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add32`
```c
uint32_t fio_u512_reduce_add32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add64`
```c
uint64_t fio_u512_reduce_add64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add8`
```c
uint8_t fio_u512_reduce_add8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and16`
```c
uint16_t fio_u512_reduce_and16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and32`
```c
uint32_t fio_u512_reduce_and32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and64`
```c
uint64_t fio_u512_reduce_and64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and8`
```c
uint8_t fio_u512_reduce_and8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul16`
```c
uint16_t fio_u512_reduce_mul16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul32`
```c
uint32_t fio_u512_reduce_mul32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul64`
```c
uint64_t fio_u512_reduce_mul64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul8`
```c
uint8_t fio_u512_reduce_mul8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or16`
```c
uint16_t fio_u512_reduce_or16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or32`
```c
uint32_t fio_u512_reduce_or32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or64`
```c
uint64_t fio_u512_reduce_or64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or8`
```c
uint8_t fio_u512_reduce_or8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub16`
```c
uint16_t fio_u512_reduce_sub16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub32`
```c
uint32_t fio_u512_reduce_sub32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub64`
```c
uint64_t fio_u512_reduce_sub64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub8`
```c
uint8_t fio_u512_reduce_sub8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor16`
```c
uint16_t fio_u512_reduce_xor16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor32`
```c
uint32_t fio_u512_reduce_xor32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor64`
```c
uint64_t fio_u512_reduce_xor64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor8`
```c
uint8_t fio_u512_reduce_xor8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_rrot16`
```c
void fio_u512_rrot16(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot32`
```c
void fio_u512_rrot32(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot64`
```c
void fio_u512_rrot64(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot8`
```c
void fio_u512_rrot8(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_sub16`
```c
void fio_u512_sub16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub32`
```c
void fio_u512_sub32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub64`
```c
void fio_u512_sub64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub8`
```c
void fio_u512_sub8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor`
```c
void fio_u512_xor(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor16`
```c
void fio_u512_xor16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor32`
```c
void fio_u512_xor32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor64`
```c
void fio_u512_xor64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor8`
```c
void fio_u512_xor8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor`
```c
void fio_u1024_3xor(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor32`
```c
void fio_u1024_3xor32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor64`
```c
void fio_u1024_3xor64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_add16`
```c
void fio_u1024_add16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add32`
```c
void fio_u1024_add32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add64`
```c
void fio_u1024_add64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add8`
```c
void fio_u1024_add8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and`
```c
void fio_u1024_and(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and16`
```c
void fio_u1024_and16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and32`
```c
void fio_u1024_and32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and64`
```c
void fio_u1024_and64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and8`
```c
void fio_u1024_and8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd16`
```c
void fio_u1024_cadd16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd32`
```c
void fio_u1024_cadd32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd64`
```c
void fio_u1024_cadd64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd8`
```c
void fio_u1024_cadd8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand16`
```c
void fio_u1024_cand16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand32`
```c
void fio_u1024_cand32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand64`
```c
void fio_u1024_cand64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand8`
```c
void fio_u1024_cand8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot16`
```c
void fio_u1024_clrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot32`
```c
void fio_u1024_clrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot64`
```c
void fio_u1024_clrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot8`
```c
void fio_u1024_clrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul16`
```c
void fio_u1024_cmul16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul32`
```c
void fio_u1024_cmul32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul64`
```c
void fio_u1024_cmul64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul8`
```c
void fio_u1024_cmul8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor16`
```c
void fio_u1024_cor16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor32`
```c
void fio_u1024_cor32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor64`
```c
void fio_u1024_cor64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor8`
```c
void fio_u1024_cor8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot16`
```c
void fio_u1024_crrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot32`
```c
void fio_u1024_crrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot64`
```c
void fio_u1024_crrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot8`
```c
void fio_u1024_crrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_csub16`
```c
void fio_u1024_csub16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub32`
```c
void fio_u1024_csub32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub64`
```c
void fio_u1024_csub64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub8`
```c
void fio_u1024_csub8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_ct_swap_if`
```c
void fio_u1024_ct_swap_if(bool cond, fio_u1024 *restrict a, fio_u1024 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor16`
```c
void fio_u1024_cxor16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor32`
```c
void fio_u1024_cxor32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor64`
```c
void fio_u1024_cxor64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor8`
```c
void fio_u1024_cxor8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_inv`
```c
void fio_u1024_inv(fio_u1024 *target, const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_is_eq`
```c
bool fio_u1024_is_eq(const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot16`
```c
void fio_u1024_lrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot32`
```c
void fio_u1024_lrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot64`
```c
void fio_u1024_lrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot8`
```c
void fio_u1024_lrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_maj`
```c
void fio_u1024_maj(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_maj32`
```c
void fio_u1024_maj32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_maj64`
```c
void fio_u1024_maj64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mul16`
```c
void fio_u1024_mul16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul32`
```c
void fio_u1024_mul32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul64`
```c
void fio_u1024_mul64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul8`
```c
void fio_u1024_mul8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mux`
```c
void fio_u1024_mux(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mux32`
```c
void fio_u1024_mux32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mux64`
```c
void fio_u1024_mux64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_or`
```c
void fio_u1024_or(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or16`
```c
void fio_u1024_or16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or32`
```c
void fio_u1024_or32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or64`
```c
void fio_u1024_or64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or8`
```c
void fio_u1024_or8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add16`
```c
uint16_t fio_u1024_reduce_add16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add32`
```c
uint32_t fio_u1024_reduce_add32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add64`
```c
uint64_t fio_u1024_reduce_add64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add8`
```c
uint8_t fio_u1024_reduce_add8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and16`
```c
uint16_t fio_u1024_reduce_and16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and32`
```c
uint32_t fio_u1024_reduce_and32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and64`
```c
uint64_t fio_u1024_reduce_and64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and8`
```c
uint8_t fio_u1024_reduce_and8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul16`
```c
uint16_t fio_u1024_reduce_mul16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul32`
```c
uint32_t fio_u1024_reduce_mul32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul64`
```c
uint64_t fio_u1024_reduce_mul64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul8`
```c
uint8_t fio_u1024_reduce_mul8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or16`
```c
uint16_t fio_u1024_reduce_or16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or32`
```c
uint32_t fio_u1024_reduce_or32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or64`
```c
uint64_t fio_u1024_reduce_or64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or8`
```c
uint8_t fio_u1024_reduce_or8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub16`
```c
uint16_t fio_u1024_reduce_sub16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub32`
```c
uint32_t fio_u1024_reduce_sub32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub64`
```c
uint64_t fio_u1024_reduce_sub64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub8`
```c
uint8_t fio_u1024_reduce_sub8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor16`
```c
uint16_t fio_u1024_reduce_xor16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor32`
```c
uint32_t fio_u1024_reduce_xor32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor64`
```c
uint64_t fio_u1024_reduce_xor64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor8`
```c
uint8_t fio_u1024_reduce_xor8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot16`
```c
void fio_u1024_rrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot32`
```c
void fio_u1024_rrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot64`
```c
void fio_u1024_rrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot8`
```c
void fio_u1024_rrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_sub16`
```c
void fio_u1024_sub16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub32`
```c
void fio_u1024_sub32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub64`
```c
void fio_u1024_sub64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub8`
```c
void fio_u1024_sub8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor`
```c
void fio_u1024_xor(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor16`
```c
void fio_u1024_xor16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor32`
```c
void fio_u1024_xor32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor64`
```c
void fio_u1024_xor64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor8`
```c
void fio_u1024_xor8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor`
```c
void fio_u2048_3xor(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor32`
```c
void fio_u2048_3xor32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor64`
```c
void fio_u2048_3xor64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_add16`
```c
void fio_u2048_add16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add32`
```c
void fio_u2048_add32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add64`
```c
void fio_u2048_add64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add8`
```c
void fio_u2048_add8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and`
```c
void fio_u2048_and(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and16`
```c
void fio_u2048_and16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and32`
```c
void fio_u2048_and32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and64`
```c
void fio_u2048_and64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and8`
```c
void fio_u2048_and8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd16`
```c
void fio_u2048_cadd16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd32`
```c
void fio_u2048_cadd32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd64`
```c
void fio_u2048_cadd64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd8`
```c
void fio_u2048_cadd8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand16`
```c
void fio_u2048_cand16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand32`
```c
void fio_u2048_cand32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand64`
```c
void fio_u2048_cand64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand8`
```c
void fio_u2048_cand8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot16`
```c
void fio_u2048_clrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot32`
```c
void fio_u2048_clrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot64`
```c
void fio_u2048_clrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot8`
```c
void fio_u2048_clrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul16`
```c
void fio_u2048_cmul16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul32`
```c
void fio_u2048_cmul32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul64`
```c
void fio_u2048_cmul64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul8`
```c
void fio_u2048_cmul8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor16`
```c
void fio_u2048_cor16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor32`
```c
void fio_u2048_cor32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor64`
```c
void fio_u2048_cor64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor8`
```c
void fio_u2048_cor8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot16`
```c
void fio_u2048_crrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot32`
```c
void fio_u2048_crrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot64`
```c
void fio_u2048_crrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot8`
```c
void fio_u2048_crrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_csub16`
```c
void fio_u2048_csub16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub32`
```c
void fio_u2048_csub32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub64`
```c
void fio_u2048_csub64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub8`
```c
void fio_u2048_csub8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_ct_swap_if`
```c
void fio_u2048_ct_swap_if(bool cond, fio_u2048 *restrict a, fio_u2048 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor16`
```c
void fio_u2048_cxor16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor32`
```c
void fio_u2048_cxor32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor64`
```c
void fio_u2048_cxor64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor8`
```c
void fio_u2048_cxor8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_inv`
```c
void fio_u2048_inv(fio_u2048 *target, const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_is_eq`
```c
bool fio_u2048_is_eq(const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot16`
```c
void fio_u2048_lrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot32`
```c
void fio_u2048_lrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot64`
```c
void fio_u2048_lrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot8`
```c
void fio_u2048_lrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_maj`
```c
void fio_u2048_maj(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_maj32`
```c
void fio_u2048_maj32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_maj64`
```c
void fio_u2048_maj64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mul16`
```c
void fio_u2048_mul16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul32`
```c
void fio_u2048_mul32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul64`
```c
void fio_u2048_mul64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul8`
```c
void fio_u2048_mul8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mux`
```c
void fio_u2048_mux(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mux32`
```c
void fio_u2048_mux32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mux64`
```c
void fio_u2048_mux64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_or`
```c
void fio_u2048_or(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or16`
```c
void fio_u2048_or16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or32`
```c
void fio_u2048_or32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or64`
```c
void fio_u2048_or64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or8`
```c
void fio_u2048_or8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add16`
```c
uint16_t fio_u2048_reduce_add16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add32`
```c
uint32_t fio_u2048_reduce_add32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add64`
```c
uint64_t fio_u2048_reduce_add64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add8`
```c
uint8_t fio_u2048_reduce_add8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and16`
```c
uint16_t fio_u2048_reduce_and16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and32`
```c
uint32_t fio_u2048_reduce_and32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and64`
```c
uint64_t fio_u2048_reduce_and64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and8`
```c
uint8_t fio_u2048_reduce_and8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul16`
```c
uint16_t fio_u2048_reduce_mul16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul32`
```c
uint32_t fio_u2048_reduce_mul32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul64`
```c
uint64_t fio_u2048_reduce_mul64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul8`
```c
uint8_t fio_u2048_reduce_mul8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or16`
```c
uint16_t fio_u2048_reduce_or16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or32`
```c
uint32_t fio_u2048_reduce_or32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or64`
```c
uint64_t fio_u2048_reduce_or64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or8`
```c
uint8_t fio_u2048_reduce_or8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub16`
```c
uint16_t fio_u2048_reduce_sub16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub32`
```c
uint32_t fio_u2048_reduce_sub32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub64`
```c
uint64_t fio_u2048_reduce_sub64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub8`
```c
uint8_t fio_u2048_reduce_sub8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor16`
```c
uint16_t fio_u2048_reduce_xor16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor32`
```c
uint32_t fio_u2048_reduce_xor32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor64`
```c
uint64_t fio_u2048_reduce_xor64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor8`
```c
uint8_t fio_u2048_reduce_xor8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot16`
```c
void fio_u2048_rrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot32`
```c
void fio_u2048_rrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot64`
```c
void fio_u2048_rrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot8`
```c
void fio_u2048_rrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_sub16`
```c
void fio_u2048_sub16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub32`
```c
void fio_u2048_sub32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub64`
```c
void fio_u2048_sub64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub8`
```c
void fio_u2048_sub8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor`
```c
void fio_u2048_xor(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor16`
```c
void fio_u2048_xor16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor32`
```c
void fio_u2048_xor32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor64`
```c
void fio_u2048_xor64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor8`
```c
void fio_u2048_xor8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor`
```c
void fio_u4096_3xor(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor32`
```c
void fio_u4096_3xor32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor64`
```c
void fio_u4096_3xor64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_add16`
```c
void fio_u4096_add16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add32`
```c
void fio_u4096_add32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add64`
```c
void fio_u4096_add64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add8`
```c
void fio_u4096_add8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and`
```c
void fio_u4096_and(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and16`
```c
void fio_u4096_and16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and32`
```c
void fio_u4096_and32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and64`
```c
void fio_u4096_and64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and8`
```c
void fio_u4096_and8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd16`
```c
void fio_u4096_cadd16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd32`
```c
void fio_u4096_cadd32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd64`
```c
void fio_u4096_cadd64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd8`
```c
void fio_u4096_cadd8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand16`
```c
void fio_u4096_cand16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand32`
```c
void fio_u4096_cand32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand64`
```c
void fio_u4096_cand64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand8`
```c
void fio_u4096_cand8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot16`
```c
void fio_u4096_clrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot32`
```c
void fio_u4096_clrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot64`
```c
void fio_u4096_clrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot8`
```c
void fio_u4096_clrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul16`
```c
void fio_u4096_cmul16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul32`
```c
void fio_u4096_cmul32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul64`
```c
void fio_u4096_cmul64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul8`
```c
void fio_u4096_cmul8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor16`
```c
void fio_u4096_cor16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor32`
```c
void fio_u4096_cor32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor64`
```c
void fio_u4096_cor64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor8`
```c
void fio_u4096_cor8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot16`
```c
void fio_u4096_crrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot32`
```c
void fio_u4096_crrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot64`
```c
void fio_u4096_crrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot8`
```c
void fio_u4096_crrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_csub16`
```c
void fio_u4096_csub16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub32`
```c
void fio_u4096_csub32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub64`
```c
void fio_u4096_csub64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub8`
```c
void fio_u4096_csub8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_ct_swap_if`
```c
void fio_u4096_ct_swap_if(bool cond, fio_u4096 *restrict a, fio_u4096 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor16`
```c
void fio_u4096_cxor16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor32`
```c
void fio_u4096_cxor32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor64`
```c
void fio_u4096_cxor64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor8`
```c
void fio_u4096_cxor8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_inv`
```c
void fio_u4096_inv(fio_u4096 *target, const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_is_eq`
```c
bool fio_u4096_is_eq(const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot16`
```c
void fio_u4096_lrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot32`
```c
void fio_u4096_lrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot64`
```c
void fio_u4096_lrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot8`
```c
void fio_u4096_lrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_maj`
```c
void fio_u4096_maj(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_maj32`
```c
void fio_u4096_maj32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_maj64`
```c
void fio_u4096_maj64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mul16`
```c
void fio_u4096_mul16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul32`
```c
void fio_u4096_mul32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul64`
```c
void fio_u4096_mul64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul8`
```c
void fio_u4096_mul8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mux`
```c
void fio_u4096_mux(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mux32`
```c
void fio_u4096_mux32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mux64`
```c
void fio_u4096_mux64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_or`
```c
void fio_u4096_or(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or16`
```c
void fio_u4096_or16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or32`
```c
void fio_u4096_or32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or64`
```c
void fio_u4096_or64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or8`
```c
void fio_u4096_or8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add16`
```c
uint16_t fio_u4096_reduce_add16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add32`
```c
uint32_t fio_u4096_reduce_add32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add64`
```c
uint64_t fio_u4096_reduce_add64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add8`
```c
uint8_t fio_u4096_reduce_add8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and16`
```c
uint16_t fio_u4096_reduce_and16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and32`
```c
uint32_t fio_u4096_reduce_and32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and64`
```c
uint64_t fio_u4096_reduce_and64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and8`
```c
uint8_t fio_u4096_reduce_and8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul16`
```c
uint16_t fio_u4096_reduce_mul16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul32`
```c
uint32_t fio_u4096_reduce_mul32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul64`
```c
uint64_t fio_u4096_reduce_mul64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul8`
```c
uint8_t fio_u4096_reduce_mul8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or16`
```c
uint16_t fio_u4096_reduce_or16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or32`
```c
uint32_t fio_u4096_reduce_or32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or64`
```c
uint64_t fio_u4096_reduce_or64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or8`
```c
uint8_t fio_u4096_reduce_or8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub16`
```c
uint16_t fio_u4096_reduce_sub16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub32`
```c
uint32_t fio_u4096_reduce_sub32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub64`
```c
uint64_t fio_u4096_reduce_sub64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub8`
```c
uint8_t fio_u4096_reduce_sub8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor16`
```c
uint16_t fio_u4096_reduce_xor16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor32`
```c
uint32_t fio_u4096_reduce_xor32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor64`
```c
uint64_t fio_u4096_reduce_xor64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor8`
```c
uint8_t fio_u4096_reduce_xor8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot16`
```c
void fio_u4096_rrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot32`
```c
void fio_u4096_rrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot64`
```c
void fio_u4096_rrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot8`
```c
void fio_u4096_rrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_sub16`
```c
void fio_u4096_sub16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub32`
```c
void fio_u4096_sub32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub64`
```c
void fio_u4096_sub64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub8`
```c
void fio_u4096_sub8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor`
```c
void fio_u4096_xor(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor16`
```c
void fio_u4096_xor16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor32`
```c
void fio_u4096_xor32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor64`
```c
void fio_u4096_xor64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor8`
```c
void fio_u4096_xor8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u128_addv64`
```c
fio_u128 fio_u128_addv64(fio_u128 a, fio_u128 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_andv`
```c
fio_u128 fio_u128_andv(fio_u128 a, fio_u128 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_orv`
```c
fio_u128 fio_u128_orv(fio_u128 a, fio_u128 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_xorv`
```c
fio_u128 fio_u128_xorv(fio_u128 a, fio_u128 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_addv64`
```c
fio_u256 fio_u256_addv64(fio_u256 a, fio_u256 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_andv`
```c
fio_u256 fio_u256_andv(fio_u256 a, fio_u256 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_orv`
```c
fio_u256 fio_u256_orv(fio_u256 a, fio_u256 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_xorv`
```c
fio_u256 fio_u256_xorv(fio_u256 a, fio_u256 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_addv64`
```c
fio_u512 fio_u512_addv64(fio_u512 a, fio_u512 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_andv`
```c
fio_u512 fio_u512_andv(fio_u512 a, fio_u512 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_orv`
```c
fio_u512 fio_u512_orv(fio_u512 a, fio_u512 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_xorv`
```c
fio_u512 fio_u512_xorv(fio_u512 a, fio_u512 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_addv64`
```c
fio_u1024 fio_u1024_addv64(fio_u1024 a, fio_u1024 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_andv`
```c
fio_u1024 fio_u1024_andv(fio_u1024 a, fio_u1024 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_orv`
```c
fio_u1024 fio_u1024_orv(fio_u1024 a, fio_u1024 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_xorv`
```c
fio_u1024 fio_u1024_xorv(fio_u1024 a, fio_u1024 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_addv64`
```c
fio_u2048 fio_u2048_addv64(fio_u2048 a, fio_u2048 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_andv`
```c
fio_u2048 fio_u2048_andv(fio_u2048 a, fio_u2048 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_orv`
```c
fio_u2048 fio_u2048_orv(fio_u2048 a, fio_u2048 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_xorv`
```c
fio_u2048 fio_u2048_xorv(fio_u2048 a, fio_u2048 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_addv64`
```c
fio_u4096 fio_u4096_addv64(fio_u4096 a, fio_u4096 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_andv`
```c
fio_u4096 fio_u4096_andv(fio_u4096 a, fio_u4096 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_orv`
```c
fio_u4096 fio_u4096_orv(fio_u4096 a, fio_u4096 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_xorv`
```c
fio_u4096 fio_u4096_xorv(fio_u4096 a, fio_u4096 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_add`
```c
uint64_t fio_u128_add(fio_u128 *result, const fio_u128 *a, const fio_u128 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u128_cmp`
```c
int fio_u128_cmp(const fio_u128 *a, const fio_u128 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u128_sub`
```c
uint64_t fio_u128_sub(fio_u128 *result, const fio_u128 *a, const fio_u128 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u256_add`
```c
uint64_t fio_u256_add(fio_u256 *result, const fio_u256 *a, const fio_u256 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u256_cmp`
```c
int fio_u256_cmp(const fio_u256 *a, const fio_u256 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u256_sub`
```c
uint64_t fio_u256_sub(fio_u256 *result, const fio_u256 *a, const fio_u256 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u512_add`
```c
uint64_t fio_u512_add(fio_u512 *result, const fio_u512 *a, const fio_u512 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u512_cmp`
```c
int fio_u512_cmp(const fio_u512 *a, const fio_u512 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u512_sub`
```c
uint64_t fio_u512_sub(fio_u512 *result, const fio_u512 *a, const fio_u512 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u1024_add`
```c
uint64_t fio_u1024_add(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u1024_cmp`
```c
int fio_u1024_cmp(const fio_u1024 *a, const fio_u1024 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u1024_sub`
```c
uint64_t fio_u1024_sub(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u2048_add`
```c
uint64_t fio_u2048_add(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u2048_cmp`
```c
int fio_u2048_cmp(const fio_u2048 *a, const fio_u2048 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u2048_sub`
```c
uint64_t fio_u2048_sub(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u4096_add`
```c
uint64_t fio_u4096_add(fio_u4096 *result, const fio_u4096 *a, const fio_u4096 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u4096_cmp`
```c
int fio_u4096_cmp(const fio_u4096 *a, const fio_u4096 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u4096_sub`
```c
uint64_t fio_u4096_sub(fio_u4096 *result, const fio_u4096 *a, const fio_u4096 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u128_montgomery_mul`
```c
void fio_u128_montgomery_mul(fio_u128 *result, const fio_u128 *a, const fio_u128 *b, const fio_u128 *N, const fio_u128 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u128_mul`
```c
void fio_u128_mul(fio_u256 *result, const fio_u128 *a, const fio_u128 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u256_montgomery_mul`
```c
void fio_u256_montgomery_mul(fio_u256 *result, const fio_u256 *a, const fio_u256 *b, const fio_u256 *N, const fio_u256 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u256_mul`
```c
void fio_u256_mul(fio_u512 *result, const fio_u256 *a, const fio_u256 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u512_montgomery_mul`
```c
void fio_u512_montgomery_mul(fio_u512 *result, const fio_u512 *a, const fio_u512 *b, const fio_u512 *N, const fio_u512 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u512_mul`
```c
void fio_u512_mul(fio_u1024 *result, const fio_u512 *a, const fio_u512 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u1024_montgomery_mul`
```c
void fio_u1024_montgomery_mul(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *N, const fio_u1024 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u1024_mul`
```c
void fio_u1024_mul(fio_u2048 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u2048_montgomery_mul`
```c
void fio_u2048_montgomery_mul(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *N, const fio_u2048 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u2048_mul`
```c
void fio_u2048_mul(fio_u4096 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_cycle_counter`
```c
inline uint64_t fio_cycle_counter(void)
```
_Symbol type:_ `function`
#### `fio_utf8_code_len`
```c
inline FIO_CONST unsigned fio_utf8_code_len(uint32_t u)
```
_Symbol type:_ `function`
#### `fio_utf8_char_len_unsafe`
```c
inline FIO_CONST unsigned fio_utf8_char_len_unsafe(uint8_t c)
```
Returns 1-4 (UTF-8 char length), 8 (middle of a char) or 0 (invalid).
_Symbol type:_ `function`
#### `fio_utf8_char_len`
```c
inline unsigned fio_utf8_char_len(const void *str_)
```
Returns the number of valid UTF-8 bytes used by first char at `str`.
_Symbol type:_ `function`
#### `fio_utf8_write`
```c
inline unsigned fio_utf8_write(void *dest_, uint32_t u)
```
Writes code point to `dest` using UFT-8. Returns number of bytes written.
_Symbol type:_ `function`
#### `fio_utf8_read`
```c
inline uint32_t fio_utf8_read(char **str)
```
Decodes the first UTF-8 char at `str` and returns its code point value.
Advances the pointer at `str` by the number of bytes consumed (read).
_Symbol type:_ `function`
#### `fio_utf8_peek`
```c
inline uint32_t fio_utf8_peek(const char *str)
```
Decodes the first UTF-8 char at `str` and returns its code point value.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/001 header.h`
12 public symbols.
### Macros
#### `FIO_MEM_REALLOC`
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) \
fio_realloc2((ptr), (new_size), (copy_len))
```
Reallocates memory, copying (at least) `copy_len` if necessary.
_Symbol type:_ `macro`
#### `FIO_MEM_FREE`
```c
#define FIO_MEM_FREE(ptr, size) fio_free((ptr))
```
Frees allocated memory.
_Symbol type:_ `macro`
#### `FIO_MEM_REALLOC_IS_SAFE`
```c
#define FIO_MEM_REALLOC_IS_SAFE fio_realloc_is_safe()
```
Set to true of internall allocator is used (memory returned set to zero).
_Symbol type:_ `macro`
#### `FIO_MEM_ALIGNMENT_SIZE`
```c
#define FIO_MEM_ALIGNMENT_SIZE fio_malloc_alignment()
```
Detect allocator allignment dynamically.
_Symbol type:_ `macro`
#### `FIO_MEM_REALLOC_ALIGNED`
```c
#define FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment) \
((ptr) = fio_realloc_aligned((ptr), (new_size), (copy_len), (alignment)))
```
Reallocates memory with an alignment requirement, assigning `ptr`.
_Symbol type:_ `macro`
#### `FIO_MEM_ALLOC_SIZE`
```c
#define FIO_MEM_ALLOC_SIZE(size) fio_alloc_size((size))
```
Returns the usable size the allocator reserves for a `size` request.
_Symbol type:_ `macro`
#### `FIO_MEM_FREE_ALIGNED`
```c
#define FIO_MEM_FREE_ALIGNED(ptr, size) fio___aligned_free_fallback((ptr))
```
Frees memory allocated using FIO_MEM_REALLOC_ALIGNED.
_Symbol type:_ `macro`
#### `FIO_MEM_SYS_ALIGN_MAX_LOG`
```c
#define FIO_MEM_SYS_ALIGN_MAX_LOG 21
```
Mirrors the custom allocator's default FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG.
_Symbol type:_ `macro`
#### `FIO_PTR_TAG`
```c
#define FIO_PTR_TAG(p) (p)
```
Supports embedded pointer tagging / untagging for the included types.
Should resolve to a tagged pointer value. i.e.: ((uintptr_t)(p) | 1)
_Symbol type:_ `macro`
#### `FIO_PTR_UNTAG`
```c
#define FIO_PTR_UNTAG(p) (p)
```
Supports embedded pointer tagging / untagging for the included types.
Should resolve to an untagged pointer value. i.e.: ((uintptr_t)(p) | ~1UL)
_Symbol type:_ `macro`
#### `FIO_PTR_TAG_VALIDATE`
```c
#define FIO_PTR_TAG_VALIDATE(ptr) ((ptr) != NULL)
```
If FIO_PTR_TAG_VALIDATE is defined, tagging will be verified before executing
any code.
FIO_PTR_TAG_VALIDATE must fail on NULL pointers.
_Symbol type:_ `macro`
#### `FIO_PTR_TAG_GET_UNTAGGED`
```c
#define FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr) \
((untagged_type *)(FIO_PTR_UNTAG((tagged_ptr))))
```
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/001 logging.h`
1 public symbols.
### Macros
#### `FIO_LOG_LEVEL_DEFAULT`
```c
#define FIO_LOG_LEVEL_DEFAULT FIO_LOG_LEVEL_DEBUG
```
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/001 memalt.h`
5 public symbols.
### Functions
#### `fio_memset`
```c
void *fio_memset(void *restrict dest, uint64_t data, size_t bytes)
```
A somewhat naive implementation of `memset`.
Probably slower than the one included with your compiler's C library.
_Symbol type:_ `function`
#### `fio_memcpy`
```c
void *fio_memcpy(void *dest_, const void *src_, size_t bytes)
```
A somewhat naive implementation of `memcpy`.
Probably slower than the one included with your compiler's C library.
_Symbol type:_ `function`
#### `fio_memchr`
```c
void *fio_memchr(const void *buffer, const char token, size_t len)
```
A token seeking function. This is a fallback for `memchr`, but `memchr`
should be faster.
_Symbol type:_ `function`
#### `fio_memcmp`
```c
int fio_memcmp(const void *a_, const void *b_, size_t len)
```
A comparison function. This is a fallback for `memcmp`, but `memcmp`
should be faster.
_Symbol type:_ `function`
#### `fio_strlen`
```c
size_t fio_strlen(const char *str)
```
An alternative to `strlen` - may raise Address Sanitation errors.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/001 patches.h`
2 public symbols.
### Functions
#### `fio_sys_env`
```c
inline char *fio_sys_env(const char *name)
```
Portable getenv: returns the value of environment variable `name`, or NULL.
Thin wrapper around getenv for API uniformity across platforms.
_Symbol type:_ `function`
#### `fio_sys_env_set`
```c
inline int fio_sys_env_set(const char *name, const char *value, int overwrite)
```
Portable setenv: sets environment variable `name` to `value`.
If `overwrite` is zero and the variable already exists, does nothing.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 atol.h`
49 public symbols.
### Macros
#### `FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER`
```c
#define FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER 1
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_MANT_MASK`
```c
#define FIO_MATH_DBL_MANT_MASK (((uint64_t)1ULL << 52) - 1)
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_EXPO_MASK`
```c
#define FIO_MATH_DBL_EXPO_MASK ((uint64_t)2047ULL << 52)
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_SIGN_MASK`
```c
#define FIO_MATH_DBL_SIGN_MASK ((uint64_t)1ULL << 63)
```
_Symbol type:_ `macro`
### Types
#### `fio_aton_s`
```c
typedef struct {
union {
int64_t i;
double f;
uint64_t u;
};
int is_float;
int err;
} fio_aton_s
```
Result type for fio_aton
_Symbol type:_ `type`
### Functions
#### `fio_aton`
```c
FIO_SFUNC fio_aton_s fio_aton(char **pstr)
```
Converts a String to a number - either an integer or a float (double).
Skips white space at the beginning of the string.
Auto detects binary and hex formats when prefix is provided (0x / 0b).
Auto detects octal when number starts with zero.
Auto detects the Strings "inf", "infinity" and "nan" as float values.
The number's format and type are returned in the return type.
If a numerical overflow or format error occurred, the `.err` flag is set.
Note: rounding errors may occur, as this is not an `strtod` exact match.
_Symbol type:_ `function`
#### `fio_atol`
```c
int64_t fio_atol(char **pstr)
```
A helper function that converts between String data to a signed int64_t.
Numbers are assumed to be in base 10. Octal (`0###`), Hex (`0x##`/`x##`) and
binary (`0b##`/ `b##`) are recognized as well. For binary Most Significant
Bit must come first.
The most significant difference between this function and `strtol` (aside of
API design), is the added support for binary representations.
_Symbol type:_ `function`
#### `fio_atof`
```c
double fio_atof(char **pstr)
```
A helper function that converts between String data to a signed double.
_Symbol type:_ `function`
#### `fio_ltoa`
```c
size_t fio_ltoa(char *dest, int64_t num, uint8_t base)
```
A helper function that writes a signed int64_t to a string.
No overflow guard is provided, make sure there's at least 68 bytes available
(for base 2).
Offers special support for base 2 (binary), base 8 (octal), base 10 and base
16 (hex) where prefixes are automatically added if required (i.e.,`"0x"` for
hex and `"0b"` for base 2, and `"0"` for octal).
Supports any base up to base 36 (using 0-9,A-Z).
An unsupported base will log an error and print zero.
Returns the number of bytes actually written (excluding the NUL terminator).
_Symbol type:_ `function`
#### `fio_ftoa`
```c
size_t fio_ftoa(char *dest, double num, uint8_t base)
```
A helper function that converts between a double to a string.
No overflow guard is provided, make sure there's at least 130 bytes
available (for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL
terminator).
_Symbol type:_ `function`
#### `fio_c2i`
```c
uint8_t fio_c2i(unsigned char c)
```
Maps characters to alphanumerical value, where numbers have their natural
values (0-9) and `A-Z` (or `a-z`) are the values 10-35.
Out of bound values return 255.
This allows parsing of numeral strings for up to base 36.
_Symbol type:_ `function`
#### `fio_i2c`
```c
uint8_t fio_i2c(unsigned char i)
```
Maps numeral values to alphanumerical characters, where numbers have their
natural values (0-9) and `A-Z` are the values 10-35.
Accepts values up to 63. Returns zero for values over 35. Out of bound values
produce undefined behavior.
This allows printing of numerals for up to base 36.
_Symbol type:_ `function`
#### `fio_digits10`
```c
inline size_t fio_digits10(int64_t i)
```
Returns the number of digits in base 10.
_Symbol type:_ `function`
#### `fio_digits10u`
```c
FIO_SFUNC size_t fio_digits10u(uint64_t i)
```
Returns the number of digits in base 10 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits8u`
```c
FIO_SFUNC size_t fio_digits8u(uint64_t i)
```
Returns the number of digits in base 8 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits16u`
```c
FIO_SFUNC size_t fio_digits16u(uint64_t i)
```
Returns the number of digits in base 16 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits_bin`
```c
FIO_SFUNC size_t fio_digits_bin(uint64_t i)
```
Returns the number of digits in base 2 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits_xbase`
```c
FIO_SFUNC size_t fio_digits_xbase(uint64_t i, size_t base)
```
Returns the number of digits in any base X<65 for an unsigned number.
_Symbol type:_ `function`
#### `fio_ltoa10`
```c
inline void fio_ltoa10(char *dest, int64_t i, size_t digits)
```
Writes a signed number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_atol10`
```c
int64_t fio_atol10(char **pstr)
```
Reads a signed base 10 formatted number.
_Symbol type:_ `function`
#### `fio_ltoa10u`
```c
inline void fio_ltoa10u(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa16u`
```c
inline void fio_ltoa16u(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa_bin`
```c
inline void fio_ltoa_bin(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa_xbase`
```c
inline void fio_ltoa_xbase(char *dest, uint64_t i, size_t digits, size_t base)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_atol8u`
```c
uint64_t fio_atol8u(char **pstr)
```
Reads a signed base 8 formatted number - may overflow buffer.
_Symbol type:_ `function`
#### `fio_atol10u`
```c
uint64_t fio_atol10u(char **pstr)
```
Reads a signed base 10 formatted number - may overflow buffer.
_Symbol type:_ `function`
#### `fio_atol16u`
```c
uint64_t fio_atol16u(char **pstr)
```
Reads an unsigned hex formatted number (possibly prefixed with "0x").
_Symbol type:_ `function`
#### `fio_atol_bin`
```c
uint64_t fio_atol_bin(char **pstr)
```
Reads an unsigned binary formatted number (possibly prefixed with "0b").
_Symbol type:_ `function`
#### `fio_atol_xbase`
```c
uint64_t fio_atol_xbase(char **pstr, size_t base)
```
Read an unsigned number in any base up to base 36.
_Symbol type:_ `function`
#### `fio_stol10u`
```c
uint64_t fio_stol10u(char **pos, const char *end)
```
Reads an unsigned base 10 number within `[pos, end)` - strict digits only
(no whitespace, sign, underscores or prefixes). Advances `pos` past the
digits consumed. On overflow sets `errno == E2BIG` and stops at the last
valid digit. Callers detect empty / trailing junk by testing `pos`.
_Symbol type:_ `function`
#### `fio_stol10`
```c
int64_t fio_stol10(char **pos, const char *end)
```
Reads a signed base 10 number within `[pos, end)` - an optional leading
`-` / `+` followed by strict digits only. Sets `errno == E2BIG` on
overflow (either the magnitude or the signed range limit).
_Symbol type:_ `function`
#### `fio_stol16u`
```c
uint64_t fio_stol16u(char **pos, const char *end)
```
Reads an unsigned hex number within `[pos, end)` - strict hex digits
only (NO `0x` prefix, no underscores). Advances `pos` past the digits
consumed. On overflow sets `errno == E2BIG` and stops at the last valid
digit.
_Symbol type:_ `function`
#### `fio_u2i_limit`
```c
inline int64_t fio_u2i_limit(uint64_t val, size_t invert)
```
Converts an unsigned `val` to a signed `val`, with overflow protection.
_Symbol type:_ `function`
#### `fio_i2d`
```c
inline double fio_i2d(int64_t mant, int64_t exponent_in_base_2)
```
Converts a 64 bit integer to an IEEE 754 formatted double.
_Symbol type:_ `function`
#### `fio_u2d`
```c
inline double fio_u2d(uint64_t mant, int64_t exponent_in_base_2)
```
Converts a 64 bit unsigned integer to an IEEE 754 formatted double.
_Symbol type:_ `function`
#### `fio_u128_hex_read`
```c
fio_u128 fio_u128_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u256_hex_read`
```c
fio_u256 fio_u256_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u512_hex_read`
```c
fio_u512 fio_u512_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u1024_hex_read`
```c
fio_u1024 fio_u1024_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u2048_hex_read`
```c
fio_u2048 fio_u2048_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u4096_hex_read`
```c
fio_u4096 fio_u4096_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u128_hex_write`
```c
size_t fio_u128_hex_write(char *dest, const fio_u128 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u256_hex_write`
```c
size_t fio_u256_hex_write(char *dest, const fio_u256 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u512_hex_write`
```c
size_t fio_u512_hex_write(char *dest, const fio_u512 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u1024_hex_write`
```c
size_t fio_u1024_hex_write(char *dest, const fio_u1024 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u2048_hex_write`
```c
size_t fio_u2048_hex_write(char *dest, const fio_u2048 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u4096_hex_write`
```c
size_t fio_u4096_hex_write(char *dest, const fio_u4096 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_ltoa8u`
```c
inline void fio_ltoa8u(char *dest, uint64_t i, size_t digits)
```
_Symbol type:_ `function`
#### `fio_d2expo`
```c
inline int fio_d2expo(double d)
```
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 crc32.h`
1 public symbols.
### Functions
#### `fio_crc32`
```c
uint32_t fio_crc32(const void *data, size_t len, uint32_t initial_crc)
```
Computes CRC32 (ITU-T V.42 / ISO 3309 / gzip polynomial 0xEDB88320).
`data` - pointer to input bytes.
`len` - number of bytes to process.
`initial_crc` - pass 0 for a new computation, or a previous return value
to continue an incremental CRC32 over multiple buffers.
Returns the CRC32 checksum.
Uses slicing-by-16 internally (~8-16x faster than byte-at-a-time).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 glob matching.h`
1 public symbols.
### Functions
#### `fio_glob_match`
```c
uint8_t fio_glob_match(fio_str_info_s pattern, fio_str_info_s string)
```
A binary glob matching helper. Returns 1 on match, otherwise returns 0.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 imap.h`
7 public symbols.
### Macros
#### `FIO_IMAP_ALWAYS_VALID`
```c
#define FIO_IMAP_ALWAYS_VALID(o) (1)
```
Helper macro for simple iMap array types - always valid.
_Symbol type:_ `macro`
#### `FIO_IMAP_VALID_NON_ZERO`
```c
#define FIO_IMAP_VALID_NON_ZERO(o) (!!((o)[0]))
```
Helper macro for simple iMap array types - valid if nonzero.
_Symbol type:_ `macro`
#### `FIO_IMAP_ALWAYS_CMP_TRUE`
```c
#define FIO_IMAP_ALWAYS_CMP_TRUE(a, b) (1)
```
Helper macro for simple iMap array types - type comparison always true.
_Symbol type:_ `macro`
#### `FIO_IMAP_ALWAYS_CMP_FALSE`
```c
#define FIO_IMAP_ALWAYS_CMP_FALSE(a, b) (0)
```
Helper macro for simple iMap array types - type comparison always false.
_Symbol type:_ `macro`
#### `FIO_IMAP_SIMPLE_CMP`
```c
#define FIO_IMAP_SIMPLE_CMP(a, b) ((a)[0] == (b)[0])
```
Helper macro for simple iMap array types - simple comparison.
_Symbol type:_ `macro`
#### `FIO_IMAP_EACH`
```c
#define FIO_IMAP_EACH(array_name, map_ptr, i) \
for (size_t i = 0; i < (map_ptr)->w; ++i) \
if (!FIO_NAME(array_name, is_valid)((map_ptr)->ary + i)) \
continue; \
else
```
Helper macro for simple iMap array types.
_Symbol type:_ `macro`
#### `FIO_TYPEDEF_IMAP_ARRAY`
```c
#define FIO_TYPEDEF_IMAP_ARRAY(array_name, \
array_type, \
imap_type, \
hash_fn, \
cmp_fn, \
is_valid_fn) \
FIO_LEAK_COUNTER_DEF(FIO_NAME(array_name, s)) \
typedef struct { \
array_type *ary; \
imap_type count; \
imap_type w; \
uint32_t capa_bits; \
} FIO_NAME(array_name, s); \
typedef struct { \
imap_type pos; \
imap_type ipos; \
imap_type set_val; \
} FIO_NAME(array_name, seeker_s); \
/** Returns the theoretical capacity for the indexed array. */ \
FIO_IFUNC int FIO_NAME(array_name, is_valid)(array_type * pobj) { \
return pobj && (!!is_valid_fn(pobj)); \
} \
/** Returns the theoretical capacity for the indexed array. */ \
FIO_IFUNC size_t FIO_NAME(array_name, capa)(FIO_NAME(array_name, s) * a) { \
if (!a || !a->capa_bits) \
return 0; \
return ((size_t)1ULL << a->capa_bits); \
} \
/** Returns a pointer to the index map. */ \
FIO_IFUNC imap_type *FIO_NAME(array_name, \
imap)(FIO_NAME(array_name, s) * a) { \
return (imap_type *)(a->ary + ((imap_type)1ULL << a->capa_bits)); \
} \
/** Deallocates dynamic memory. */ \
FIO_IFUNC void FIO_NAME(array_name, destroy)(FIO_NAME(array_name, s) * a) { \
size_t capa = FIO_NAME(array_name, capa)(a); \
if (a->ary) { \
FIO_LEAK_COUNTER_ON_FREE(FIO_NAME(array_name, s)); \
FIO_TYPEDEF_IMAP_FREE( \
a->ary, \
(capa * (sizeof(*a->ary)) + (capa * (sizeof(imap_type))))); \
} \
*a = (FIO_NAME(array_name, s)){0}; \
(void)capa; /* if unused */ \
} \
/** Allocates dynamic memory. */ \
FIO_IFUNC int FIO_NAME(array_name, __alloc)(FIO_NAME(array_name, s) * a, \
size_t bits) { \
if (!bits || bits > ((sizeof(imap_type) << 3) - 2)) \
return -1; \
size_t capa = 1ULL << bits; \
if (bits > (size_t)(FIO_MAP_WARNING_BITSIZE - 1)) \
FIO_LOG_WARNING( \
"The " #array_name "_s map is now using a LOT of memory - %zu Mb!", \
(capa >> 20) * (sizeof(array_type) + sizeof(imap_type))); \
size_t old_capa = FIO_NAME(array_name, capa)(a); \
array_type *tmp = (array_type *)FIO_TYPEDEF_IMAP_REALLOC( \
a->ary, \
(a->capa_bits ? (old_capa * (sizeof(array_type)) + \
(old_capa * (sizeof(imap_type)))) \
: 0), \
(capa * (sizeof(array_type)) + (capa * (sizeof(imap_type)))), \
(a->w * (sizeof(array_type)))); \
(void)old_capa; /* if unused */ \
if (!tmp) \
return -1; \
if (!a->ary) \
FIO_LEAK_COUNTER_ON_ALLOC(FIO_NAME(array_name, s)); \
a->capa_bits = (uint32_t)bits; \
a->ary = tmp; \
if (!FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE) { \
FIO_MEMSET((tmp + a->w), 0, ((capa - a->w) * (sizeof(*tmp)))); \
FIO_MEMSET((tmp + capa), 0, (capa * (sizeof(imap_type)))); \
} \
return 0; \
} \
/** Returns the index map position and array position of a value, if any. */ \
FIO_SFUNC FIO_NAME(array_name, seeker_s) \
FIO_NAME(array_name, seek)(FIO_NAME(array_name, s) * a, \
array_type * pobj) { \
FIO_NAME(array_name, seeker_s) \
r = {0, ((imap_type) ~(imap_type)0), ((imap_type) ~(imap_type)0)}; \
if (!a || ((!a->capa_bits) | (!a->ary))) \
return r; \
r.pos = a->w; \
imap_type capa = (imap_type)1UL << a->capa_bits; \
imap_type *imap = (imap_type *)(a->ary + capa); \
const imap_type pos_mask = (imap_type)(capa - (imap_type)1); \
const imap_type hash_mask = (imap_type)~pos_mask; \
const imap_type hash = (imap_type)hash_fn(pobj); \
imap_type tester = (hash & hash_mask); /* hides lower bits for `tester` */ \
imap_type pos = hash + (hash >> a->capa_bits); /* use more bits for pos */ \
tester += (imap_type)((!tester) << a->capa_bits); \
tester -= (imap_type)((hash_mask == tester) << a->capa_bits); \
size_t attempts = 11; \
for (;;) { \
/* tests up to 3 groups of 4 bytes (uint32_t) within a 64 byte group */ \
for (size_t mini_steps = 0;;) { \
pos &= pos_mask; \
const imap_type pos_hash = imap[pos] & hash_mask; \
const imap_type pos_index = imap[pos] & pos_mask; \
if ((pos_hash == tester) && cmp_fn((a->ary + pos_index), pobj)) { \
r.ipos = pos; \
r.pos = pos_index; \
r.set_val = tester | pos_index; \
return r; \
} \
if (!imap[pos]) { \
r.ipos = pos; \
r.set_val = tester | r.pos; /* r.pos == a->w */ \
return r; \
} \
if (imap[pos] == (imap_type)(~(imap_type)0)) { \
r.ipos = pos; \
r.set_val = tester | r.pos; /* r.pos == a->w */ \
} \
if (!((--attempts))) \
return r; \
if (mini_steps == 2) \
break; \
pos += 3 + mini_steps; /* 0, 3, 7 = max of 56 byte distance */ \
++mini_steps; \
} \
pos += (imap_type)0xC19F5985UL; /* big step */ \
} \
} \
/** fills an empty imap with the info about existing elements. */ \
FIO_SFUNC int FIO_NAME(array_name, \
__fill_imap)(FIO_NAME(array_name, s) * a) { \
if (!a->count) { \
a->w = 0; \
return 0; \
} \
imap_type *imap = FIO_NAME(array_name, imap)(a); \
if (a->count != a->w) { \
a->count = 0; \
for (size_t i = 0; i < a->w; ++i) { \
if (!is_valid_fn((a->ary + i))) \
continue; \
if (a->count != i) \
a->ary[a->count] = a->ary[i]; \
++a->count; \
} \
} \
for (a->w = 0; a->w < a->count; ++(a->w)) { \
FIO_NAME(array_name, seeker_s) \
s = FIO_NAME(array_name, seek)(a, a->ary + a->w); \
if (s.pos != a->w || s.ipos == (imap_type)(~(imap_type)0)) { \
a->w = a->count; \
return -1; /* destination not big enough to contain collisions! */ \
} \
imap[s.ipos] = s.set_val; \
} \
a->w = a->count; \
return 0; \
} \
/** expands the existing array & imap storage capacity. */ \
FIO_IFUNC int FIO_NAME(array_name, __expand)(FIO_NAME(array_name, s) * a) { \
for (;;) { \
if (FIO_NAME(array_name, __alloc)(a, \
a->capa_bits + 1 + (!a->capa_bits))) \
return -1; \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return 0; \
} \
} \
/** Reserves a minimum imap storage capacity. */ \
FIO_IFUNC int FIO_NAME(array_name, reserve)(FIO_NAME(array_name, s) * a, \
imap_type min) { \
imap_type bits = 2; \
if (min > ((imap_type)~0ULL) >> 1) \
return -1; \
while ((1ULL << bits) < min) \
++bits; \
if (bits <= a->capa_bits) \
return 0; \
if (FIO_NAME(array_name, __alloc)(a, bits)) \
return -1; \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return 0; \
return FIO_NAME(array_name, __expand)(a); \
} \
/** Rehashes the array and fills the imap (use after sorting). */ \
FIO_IFUNC int FIO_NAME(array_name, rehash)(FIO_NAME(array_name, s) * a) { \
if (!a || !a->ary) \
return -1; \
size_t bytes = sizeof(imap_type) * ((size_t)1ULL << a->capa_bits); \
imap_type *imap = FIO_NAME(array_name, imap)(a); \
FIO_MEMSET(imap, 0, bytes); \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return -1; \
return 0; \
} \
/** Sets an object in the Array. Optionally overwrites existing data. */ \
FIO_IFUNC array_type *FIO_NAME(array_name, set)(FIO_NAME(array_name, s) * a, \
array_type obj, \
int overwrite) { \
if (!a || !is_valid_fn((&obj))) \
return NULL; \
{ \
size_t capa = FIO_NAME(array_name, capa)(a); \
if (a->w == capa) \
FIO_NAME(array_name, __expand)(a); \
else if (a->count != a->w && \
(a->w + (a->w >> 1)) > FIO_NAME(array_name, capa)(a)) { \
FIO_MEMSET((a->ary + capa), 0, (capa * (sizeof(imap_type)))); \
FIO_NAME(array_name, __fill_imap)(a); \
} \
} \
for (;;) { \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.ipos == (imap_type)(~(imap_type)0)) { /* no room in the imap */ \
FIO_NAME(array_name, __expand)(a); \
continue; \
} \
if (s.pos == a->w) { /* new object */ \
a->ary[a->w] = obj; \
++a->w; \
++a->count; \
FIO_NAME(array_name, imap)(a)[s.ipos] = s.set_val; \
return a->ary + s.pos; \
} \
FIO_ASSERT_DEBUG(s.pos < a->w && s.ipos < FIO_NAME(array_name, capa)(a), \
"WTF?"); \
if (!overwrite) \
return a->ary + s.pos; \
a->ary[s.pos] = obj; \
return a->ary + s.pos; \
} \
} \
/** Finds an object in the Array using the index map. */ \
FIO_IFUNC array_type *FIO_NAME(array_name, get)(FIO_NAME(array_name, s) * a, \
array_type obj) { \
if (!a || !is_valid_fn((&obj))) \
return NULL; \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.pos >= a->w) \
return NULL; \
return a->ary + s.pos; \
} \
/** Removes an object in the Array's index map, zeroing out its memory. */ \
FIO_IFUNC int FIO_NAME(array_name, remove)(FIO_NAME(array_name, s) * a, \
array_type obj) { \
if (!a || !is_valid_fn((&obj))) \
return -1; \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.pos >= a->w) \
return -1; \
a->ary[s.pos] = (array_type){0}; \
FIO_NAME(array_name, imap)(a)[s.ipos] = (imap_type)(~(imap_type)0); \
--a->count; \
while (a->w && !is_valid_fn((a->ary + a->w - 1))) \
--a->w; \
return 0; \
}
```
This MACRO defines the type and functions needed for an indexed array.
This is used internally and documentation is poor.
An indexed array is simple ordered array who's objects are indexed using an
almost-hash map, allowing for easy seeking while also enjoying an array's
advantages.
The index map uses one `imap_type` (i.e., `uint64_t`) to store both the array
index and any leftover hash data (the first half being tested during the
random access and the leftover during comparison). The reserved value `0`
indicates a free slot. The reserved value `~0` indicates a freed item (a free
slot that was previously used).
- `array_name_s` the main array container (.ary is the array itself)
- `array_name_seeker_s` is a seeker type that finds objects.
- `array_name_seek` finds an object or its future position.
- `array_name_capa` the imap's theoretical storage capacity.
- `array_name_set` writes or overwrites data to the array.
- `array_name_get` returns a pointer to the object within the array.
- `array_name_remove` removes an object and resets its memory to zero.
- `array_name_reserve` reserves a minimum imap storage capacity.
- `array_name_rehash` re-builds the imap (use after sorting).
- `array_name_destroy` de-allocates internal dynamic memory.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/002 math.h`
6 public symbols.
### Functions
#### `fio_math_div`
```c
inline void fio_math_div(uint64_t *dest, uint64_t *reminder, const uint64_t *a, const uint64_t *b, const size_t number_array_length)
```
Multi-precision DIV for `len*64` bit long a, b.
This is NOT constant time.
The algorithm might be slow, as my math isn't that good and I couldn't
understand faster division algorithms (such as Newton–Raphson division)... so
this is sort of a factorized variation on long division.
_Symbol type:_ `function`
#### `fio_math_shr`
```c
inline void fio_math_shr(uint64_t *dest, uint64_t *n, const size_t right_shift_bits, size_t number_array_length)
```
Multi-precision shift right for `len` word number `n`.
_Symbol type:_ `function`
#### `fio_math_shl`
```c
inline void fio_math_shl(uint64_t *dest, uint64_t *n, const size_t left_shift_bits, const size_t number_array_length)
```
Multi-precision shift left for `len*64` bit number `n`.
_Symbol type:_ `function`
#### `fio_math_inv`
```c
inline void fio_math_inv(uint64_t *dest, uint64_t *n, size_t len)
```
Multi-precision Inverse for `len*64` bit number `n` (turn `1` into `-1`).
_Symbol type:_ `function`
#### `fio_math_msb_index`
```c
FIO_MIFN size_t fio_math_msb_index(uint64_t *n, const size_t len)
```
Multi-precision - returns the index for the most significant bit or -1.
_Symbol type:_ `function`
#### `fio_math_lsb_index`
```c
FIO_MIFN size_t fio_math_lsb_index(uint64_t *n, const size_t len)
```
Multi-precision - returns the index for the least significant bit or -1.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 random.h`
15 public symbols.
### Macros
#### `FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH`
```c
#define FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH 0
```
_Symbol type:_ `macro`
### Functions
#### `fio_rand64`
```c
uint64_t fio_rand64(void)
```
Returns 64 psedo-random bits. Probably not cryptographically safe.
_Symbol type:_ `function`
#### `fio_rand128`
```c
fio_u128 fio_rand128(void)
```
Returns 64 psedo-random bits. Probably not cryptographically safe.
_Symbol type:_ `function`
#### `fio_rand_bytes`
```c
void fio_rand_bytes(void *target, size_t len)
```
Writes `len` bytes of psedo-random bits to the target buffer.
_Symbol type:_ `function`
#### `fio_rand_bytes_secure`
```c
int fio_rand_bytes_secure(void *target, size_t len)
```
Writes `len` bytes of cryptographically secure random data to `target`.
Uses the system CSPRNG: arc4random_buf() on BSD/macOS, BCryptGenRandom() on
Windows (MSVC links bcrypt.lib automatically; MinGW must link `-lbcrypt`),
getrandom() on Linux, or /dev/urandom as fallback.
Returns 0 on success, -1 on failure (never partial success; on failure the
`target` content MUST NOT be used). A NULL `target` with `len > 0` fails.
IMPORTANT: Use this for security-sensitive operations like key generation.
_Symbol type:_ `function`
#### `fio_rand_reseed`
```c
void fio_rand_reseed(void)
```
Reseeds the random engine using system state (rusage / jitter).
_Symbol type:_ `function`
#### `fio_risky_hash`
```c
uint64_t fio_risky_hash(const void *buf, size_t len, uint64_t seed)
```
Computes a facil.io Risky Hash (Risky v.3).
_Symbol type:_ `function`
#### `fio_risky_ptr`
```c
inline uint64_t fio_risky_ptr(void *ptr)
```
Adds a bit of entropy to pointer values. Designed to be unsafe.
_Symbol type:_ `function`
#### `fio_risky_num`
```c
inline uint64_t fio_risky_num(uint64_t number, uint64_t seed)
```
Adds a bit of entropy to numeral values. Designed to be unsafe.
_Symbol type:_ `function`
#### `fio_stable_hash`
```c
uint64_t fio_stable_hash(const void *data, size_t len, uint64_t seed)
```
Computes a facil.io Stable Hash (will not be updated, even if broken).
_Symbol type:_ `function`
#### `fio_stable_hash128`
```c
void fio_stable_hash128(void *restrict dest, const void *restrict data, size_t len, uint64_t seed)
```
Computes a facil.io Stable Hash (will not be updated, even if broken).
_Symbol type:_ `function`
#### `fio_risky256`
```c
fio_u256 fio_risky256(const void *data, uint64_t len)
```
Computes a 256-bit non-cryptographic hash (RiskyHash 256).
Based on the A3 (Zero-Copy ILP) design: 2-state multiply-fold with
cross-lane mixing, 128 bytes/iteration, zero-copy XOR from input.
Returns a `fio_u256` (32 bytes). Passes strict avalanche (50.0%),
collision, differential, and length independence tests.
_Symbol type:_ `function`
#### `fio_risky512`
```c
fio_u512 fio_risky512(const void *data, uint64_t len)
```
Computes a 512-bit non-cryptographic hash (RiskyHash 512).
SHAKE-style extension of fio_risky256: the first 256 bits of the 512-bit
output are identical to fio_risky256 (truncation-safe). The second 256 bits
come from an additional squeeze round.
Returns a `fio_u512` (64 bytes).
_Symbol type:_ `function`
#### `fio_risky256_hmac`
```c
fio_u256 fio_risky256_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
Computes HMAC-RiskyHash-256 (RFC 2104 construction, 32-byte digest).
Uses fio_risky256 as the underlying hash with a 64-byte block size.
If `key_len > 64`, the key is hashed first with fio_risky256.
NOTE: The underlying hash is non-cryptographic, so standard HMAC
security proofs do not apply. For cryptographic HMAC, use blake2.
_Symbol type:_ `function`
#### `fio_risky512_hmac`
```c
fio_u512 fio_risky512_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
Computes HMAC-RiskyHash-512 (RFC 2104 construction, 64-byte digest).
Uses fio_risky512 as the underlying hash with a 64-byte block size.
If `key_len > 64`, the key is hashed first with fio_risky512.
NOTE: The underlying hash is non-cryptographic, so standard HMAC
security proofs do not apply. For cryptographic HMAC, use blake2.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 signals.h`
8 public symbols.
### Macros
#### `FIO_SIGNAL_USER1`
```c
#define FIO_SIGNAL_USER1 SIGUSR1
```
_Symbol type:_ `macro`
#### `FIO_SIGNAL_USER2`
```c
#define FIO_SIGNAL_USER2 SIGUSR2
```
_Symbol type:_ `macro`
#### `FIO_SIGNAL_USER_UNREGISTERED`
```c
#define FIO_SIGNAL_USER_UNREGISTERED SIGUSR2
```
_Symbol type:_ `macro`
### Types
#### `fio_signal_monitor_args_s`
```c
typedef struct {
/** The signal number to listen for. */
int sig;
/** The callback to run - leave NULL to ignore signal. */
void (*callback)(int sig, void *udata);
/** Opaque user data. */
void *udata;
/** Should the signal propagate to existing handler(s)? */
bool propagate;
/** Call (safe) callback immediately? or wait for `fio_signal_review`? */
bool immediate;
} fio_signal_monitor_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_signal_monitor`
```c
int fio_signal_monitor(fio_signal_monitor_args_s args)
```
Starts to monitor for the specified signal, setting an optional callback.
_Symbol type:_ `function`
#### `fio_signal_monitor`
```c
#define fio_signal_monitor(...) \
fio_signal_monitor((fio_signal_monitor_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_signal_review`
```c
int fio_signal_review(void)
```
Reviews all signals, calling any relevant callbacks.
_Symbol type:_ `function`
#### `fio_signal_forget`
```c
int fio_signal_forget(int sig)
```
Stops monitoring the specified signal.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 sort.h`
3 public symbols.
### Macros
#### `FIO_SORT_THRESHOLD`
```c
#define FIO_SORT_THRESHOLD 96
```
The default threshold below which quicksort delegates to insert sort.
_Symbol type:_ `macro`
#### `FIO_SORT_SWAP`
```c
#define FIO_SORT_SWAP(a, b) \
do { \
FIO_SORT_TYPE tmp__ = (a); \
(a) = (b); \
(b) = tmp__; \
} while (0)
```
Default swap operation assumes an array and swaps array members
_Symbol type:_ `macro`
#### `FIO_SORT_IS_BIGGER`
```c
#define FIO_SORT_IS_BIGGER(a, b) ((a) > (b))
```
MUST evaluate as 1 if a > b (zero if equal or smaller).
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/002 threads.h`
33 public symbols.
### Macros
#### `FIO_THREAD_MUTEX_INIT`
```c
#define FIO_THREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
```
Used this macro for static initialization.
_Symbol type:_ `macro`
#### `FIO_MEMCPY_THREADS`
```c
#define FIO_MEMCPY_THREADS 8
```
_Symbol type:_ `macro`
### Types
#### `fio_thread_t`
```c
typedef pthread_t fio_thread_t
```
_Symbol type:_ `type`
#### `fio_thread_pid_t`
```c
typedef pid_t fio_thread_pid_t
```
_Symbol type:_ `type`
#### `fio_thread_mutex_t`
```c
typedef pthread_mutex_t fio_thread_mutex_t
```
_Symbol type:_ `type`
#### `fio_thread_cond_t`
```c
typedef pthread_cond_t fio_thread_cond_t
```
_Symbol type:_ `type`
#### `fio_thread_priority_e`
```c
typedef enum {
FIO_THREAD_PRIORITY_ERROR = -1,
FIO_THREAD_PRIORITY_LOWEST = 0,
FIO_THREAD_PRIORITY_LOW,
FIO_THREAD_PRIORITY_NORMAL,
FIO_THREAD_PRIORITY_HIGH,
FIO_THREAD_PRIORITY_HIGHEST,
} fio_thread_priority_e
```
Possible thread priority values.
_Symbol type:_ `type`
### Functions
#### `fio_thread_fork`
```c
inline fio_thread_pid_t fio_thread_fork(void)
```
Should behave the same as the POSIX system call `fork`.
_Symbol type:_ `function`
#### `fio_thread_getpid`
```c
inline fio_thread_pid_t fio_thread_getpid(void)
```
Should behave the same as the POSIX system call `getpid`.
_Symbol type:_ `function`
#### `fio_thread_kill`
```c
inline int fio_thread_kill(fio_thread_pid_t pid, int sig)
```
Should behave the same as the POSIX system call `kill`.
_Symbol type:_ `function`
#### `fio_thread_waitpid`
```c
inline int fio_thread_waitpid(fio_thread_pid_t pid, int *stat_loc, int options)
```
Should behave the same as the POSIX system call `waitpid`.
_Symbol type:_ `function`
#### `fio_thread_create`
```c
inline int fio_thread_create(fio_thread_t *t, void *(*fn)(void *), void *arg)
```
Starts a new thread, returns 0 on success and -1 on failure.
_Symbol type:_ `function`
#### `fio_thread_join`
```c
inline int fio_thread_join(fio_thread_t *t)
```
Waits for the thread to finish.
_Symbol type:_ `function`
#### `fio_thread_detach`
```c
inline int fio_thread_detach(fio_thread_t *t)
```
Detaches the thread, so thread resources are freed automatically.
_Symbol type:_ `function`
#### `fio_thread_exit`
```c
inline void fio_thread_exit(void)
```
Ends the current running thread.
_Symbol type:_ `function`
#### `fio_thread_equal`
```c
inline int fio_thread_equal(fio_thread_t *a, fio_thread_t *b)
```
_Symbol type:_ `function`
#### `fio_thread_current`
```c
inline fio_thread_t fio_thread_current(void)
```
Returns the current thread.
_Symbol type:_ `function`
#### `fio_thread_nid`
```c
inline uintptr_t fio_thread_nid(void)
```
Returns a process-local numeral ID for the current thread.
The value is stable for the thread's lifetime and unique among live threads.
It may be reused after the thread exits.
_Symbol type:_ `function`
#### `fio_thread_yield`
```c
inline void fio_thread_yield(void)
```
Yields thread execution.
_Symbol type:_ `function`
#### `fio_thread_priority`
```c
FIO_SFUNC fio_thread_priority_e fio_thread_priority(void)
```
Returns a thread's priority level.
_Symbol type:_ `function`
#### `fio_thread_priority_set`
```c
FIO_SFUNC int fio_thread_priority_set(fio_thread_priority_e)
```
Sets a thread's priority level.
_Symbol type:_ `function`
#### `fio_thread_mutex_init`
```c
inline int fio_thread_mutex_init(fio_thread_mutex_t *m)
```
Initializes a simple Mutex.
Or use the static initialization value: FIO_THREAD_MUTEX_INIT
_Symbol type:_ `function`
#### `fio_thread_mutex_lock`
```c
inline int fio_thread_mutex_lock(fio_thread_mutex_t *m)
```
Locks a simple Mutex, returning -1 on error.
_Symbol type:_ `function`
#### `fio_thread_mutex_trylock`
```c
inline int fio_thread_mutex_trylock(fio_thread_mutex_t *m)
```
Attempts to lock a simple Mutex, returning zero on success.
_Symbol type:_ `function`
#### `fio_thread_mutex_unlock`
```c
inline int fio_thread_mutex_unlock(fio_thread_mutex_t *m)
```
Unlocks a simple Mutex, returning zero on success or -1 on error.
_Symbol type:_ `function`
#### `fio_thread_mutex_destroy`
```c
inline void fio_thread_mutex_destroy(fio_thread_mutex_t *m)
```
Destroys the simple Mutex (cleanup).
_Symbol type:_ `function`
#### `fio_thread_cond_init`
```c
inline int fio_thread_cond_init(fio_thread_cond_t *c)
```
Initializes a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_wait`
```c
inline int fio_thread_cond_wait(fio_thread_cond_t *c, fio_thread_mutex_t *m)
```
Waits on a conditional variable (MUST be previously locked).
_Symbol type:_ `function`
#### `fio_thread_cond_timedwait`
```c
inline int fio_thread_cond_timedwait(fio_thread_cond_t *c, fio_thread_mutex_t *m, size_t milliseconds)
```
Waits on a conditional variable (MUST be previously locked).
_Symbol type:_ `function`
#### `fio_thread_cond_signal`
```c
inline int fio_thread_cond_signal(fio_thread_cond_t *c)
```
Signals a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_broadcast`
```c
inline int fio_thread_cond_broadcast(fio_thread_cond_t *c)
```
Signal broadcast for a conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_destroy`
```c
inline void fio_thread_cond_destroy(fio_thread_cond_t *c)
```
Destroys a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_memcpy`
```c
FIO_SFUNC size_t fio_thread_memcpy(void *restrict dest, const void *restrict src, size_t bytes)
```
Multi-threaded memcpy using up to FIO_MEMCPY_THREADS threads
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 url.h`
7 public symbols.
### Macros
#### `FIO_URL_QUERY_EACH`
```c
#define FIO_URL_QUERY_EACH(query_buf, i) \
for (fio_url_query_each_s i = fio_url_query_each_next( \
(fio_url_query_each_s){.private___ = (query_buf)}); \
i.name.buf; \
i = fio_url_query_each_next(i))
```
Iterates through each of the query elements.
_Symbol type:_ `macro`
### Types
#### `fio_url_s`
```c
typedef struct {
fio_buf_info_s scheme;
fio_buf_info_s user;
fio_buf_info_s password;
fio_buf_info_s host;
fio_buf_info_s port;
fio_buf_info_s path;
fio_buf_info_s query;
fio_buf_info_s target;
} fio_url_s
```
the result returned by `fio_url_parse`
_Symbol type:_ `type`
#### `fio_url_query_each_s`
```c
typedef struct {
fio_buf_info_s name;
fio_buf_info_s value;
fio_buf_info_s private___;
} fio_url_query_each_s
```
The type used by the `FIO_URL_QUERY_EACH` iterator macro.
_Symbol type:_ `type`
#### `fio_url_tls_info_s`
```c
typedef struct {
fio_buf_info_s key;
fio_buf_info_s cert;
fio_buf_info_s trust;
fio_buf_info_s pass;
bool tls;
} fio_url_tls_info_s
```
_Symbol type:_ `type`
### Functions
#### `fio_url_parse`
```c
fio_url_s fio_url_parse(const char *url, size_t len)
```
Parses the URI returning it's components and their lengths (no decoding
performed, doesn't accept decoded URIs).
The returned string are NOT NUL terminated, they are merely locations within
the original string.
This function attempts to accept many different formats, including any of the
following:
* `/complete_path?query#target`
i.e.: /index.html?page=1#list
* `host:port/complete_path?query#target`
i.e.:
example.com
example.com:8080
example.com/index.html
example.com:8080/index.html
example.com:8080/index.html?key=val#target
* `user:password@host:port/path?query#target`
i.e.: user:1234@example.com:8080/index.html
* `username[:password]@host[:port][...]`
i.e.: john:1234@example.com
* `schema://user:password@host:port/path?query#target`
i.e.: http://example.com/index.html?page=1#list
For performance reasons, no format validation is performed. Function assumes
that the `url` string is `len` long and contains a valid URL. Invalid formats
might produce unexpected results.
NOTE: the `unix`, `file` and `priv` schemas are reserved for file paths.
_Symbol type:_ `function`
#### `fio_url_query_each_next`
```c
FIO_SFUNC fio_url_query_each_s fio_url_query_each_next(fio_url_query_each_s)
```
A helper function for the `FIO_URL_QUERY_EACH` macro implementation.
_Symbol type:_ `function`
#### `fio_url_is_tls`
```c
fio_url_tls_info_s fio_url_is_tls(fio_url_s u)
```
Returns TLS data associated with the URL.
This function supports implicit TLS by scheme data for the following possible
values:
- `wss` - Secure WebSockets.
- `sses` - Secure SSE (Server Sent Events).
- `https` - Secure HTTP.
- `tcps` - Secure TCP/IP.
- `tls` - Secure TCP/IP.
- `udps` - Secure UDP.
i.e.:
tls://example.com/
tcps://example.com/
udps://example.com/
wss://example.com/
https://example.com/
sses://example.com/
This function also supports explicit TLS by query data for the following
possible key-pair values:
- `tls` - self-signed TLS (unless key / cert are provided).
- `tls=true` - self-signed TLS (unless key / cert are provided).
- `tls=` - key and certificate files (same path, different
file extensions).
- `key=` - path or env variable name for the private key.
- `cert=` - path or env variable name for the public
certificate.
- `pass` - password for decrypting key / cert data.
i.e.:
tcp://example.com/?tls (anonymous TLS)
udp://example.com/?tls=true
https://example.com/?tls=key_cert_folder_or_prefix&pass=key_password
https://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
wss://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
tcp://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/003 entities.h`
1 public symbols.
### Functions
#### `fio_entity`
```c
size_t fio_entity(char *dest, const char *src, size_t len)
```
Decodes a single ML entity from `src` (length `len`, starting at '&').
Writes decoded UTF-8 bytes to `dest`, which MUST be at least 8 bytes.
A NUL terminator is written only if the decoded result is shorter than
8 bytes.
Returns the number of bytes written to `dest` (excluding NUL), or 0 if
`src` does not start with a valid entity.
Supported forms:
- Named: `&name;` (case-insensitive, ~40 common entities)
- Decimal: `digits;`
- Hex: `hex;` or `hex;`
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 files.h`
28 public symbols.
### Macros
#### `FIO_FILENAME_PATH_CAPA`
```c
#define FIO_FILENAME_PATH_CAPA (PATH_MAX | 4096)
```
_Symbol type:_ `macro`
#### `FIO_FD_FIND_EOF`
```c
#define FIO_FD_FIND_EOF ((size_t)-1)
```
End of file value for `fio_fd_find_next`
_Symbol type:_ `macro`
#### `FIO_FD_FIND_BLOCK`
```c
#define FIO_FD_FIND_BLOCK 4096
```
Size on the stack used by `fio_fd_find_next` for each read cycle.
_Symbol type:_ `macro`
#### `FIO_FOLDER_SEPARATOR`
```c
#define FIO_FOLDER_SEPARATOR '\\'
```
_Symbol type:_ `macro`
### Types
#### `fio_filename_remove_args_s`
```c
typedef struct {
/** The path of the file / folder to remove. */
const char *path;
/** Set to allow the removal of an (empty) folder. */
uint8_t folder;
/** Set to remove a folder and all of its content (implies `folder`). */
uint8_t recursive;
} fio_filename_remove_args_s
```
Arguments for `fio_filename_remove`.
_Symbol type:_ `type`
#### `fio_filename_make_path_args_s`
```c
typedef struct {
/** The folder path to create (nested folders allowed). */
const char *path;
/** The creation mode (POSIX only); zero (0) defaults to 0755. */
uint32_t mode;
} fio_filename_make_path_args_s
```
Arguments for `fio_filename_make_path`.
_Symbol type:_ `type`
#### `fio_filename_s`
```c
typedef struct {
fio_buf_info_s folder;
fio_buf_info_s basename;
fio_buf_info_s ext;
} fio_filename_s
```
A result type for the filename parsing helper.
_Symbol type:_ `type`
### Functions
#### `fio_filename_open`
```c
int fio_filename_open(const char *filename, int flags)
```
Opens `filename`, returning the same as values as `open` on POSIX systems.
If `path` starts with a `"~/"` than it will be relative to the user's home
folder (on Windows, testing for `"~\"`).
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
_Symbol type:_ `function`
#### `fio_filename_is_unsafe`
```c
int fio_filename_is_unsafe(const char *path)
```
Returns 1 if `path` does folds backwards (OS separator dependent).
_Symbol type:_ `function`
#### `fio_filename_is_unsafe_url`
```c
int fio_filename_is_unsafe_url(const char *path)
```
Returns 1 if `path` does folds backwards (has "/../" or "//").
_Symbol type:_ `function`
#### `fio_filename_tmp`
```c
int fio_filename_tmp(void)
```
Creates a temporary file, returning its file descriptor.
_Symbol type:_ `function`
#### `fio_filename_remove`
```c
int fio_filename_remove(fio_filename_remove_args_s args)
```
Removes the file / link or folder at `path`.
* By default (no flags), removes a file / link (like `unlink`).
* With `folder` set, removes an empty folder (like `rmdir`).
* With `recursive` set (implies `folder`), removes a folder and all of its
content (like `rm -r`). If `path` isn't a folder, the `recursive` flag
is ignored and `path` is removed as a file / link.
Links are removed, never followed into.
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
Returns 0 on success and -1 on error. Recursive removal stops on the first
error (some content may remain).
_Symbol type:_ `function`
#### `fio_filename_remove`
```c
#define fio_filename_remove(...) \
fio_filename_remove((fio_filename_remove_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_filename_make_path`
```c
int fio_filename_make_path(fio_filename_make_path_args_s args)
```
Creates the folder at `path`, including any missing parent folders
(similar to `mkdir -p`).
An existing folder is NOT an error. A non-folder component along the way IS
an error (`errno == ENOTDIR`).
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
Returns 0 on success and -1 on error.
_Symbol type:_ `function`
#### `fio_filename_make_path`
```c
#define fio_filename_make_path(...) \
fio_filename_make_path((fio_filename_make_path_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_filename_overwrite`
```c
inline int fio_filename_overwrite(const char *filename, const void *buf, size_t len)
```
Overwrites `filename` with the data in the buffer.
If `path` starts with a `"~/"` than it will be relative to the user's home
folder (on Windows, testing for `"~\"`).
Returns -1 on error or 0 on success. On error, the state of the file is
undefined (may be doesn't exit / nothing written / partially written).
_Symbol type:_ `function`
#### `fio_filename_size`
```c
inline size_t fio_filename_size(const char *filename)
```
Returns the file size (or 0 on both error / empty file).
_Symbol type:_ `function`
#### `fio_fd_size`
```c
inline size_t fio_fd_size(int fd)
```
Returns the file size (or 0 on both error / empty file).
_Symbol type:_ `function`
#### `fio_filename_type`
```c
inline size_t fio_filename_type(const char *filename)
```
Returns the file type (or 0 on both error).
See: https://www.man7.org/linux/man-pages/man7/inode.7.html
_Symbol type:_ `function`
#### `fio_filename_stat`
```c
inline int fio_filename_stat(const char *filename, struct stat *stat_buf)
```
Populates `stat_buf` with the file's metadata. Returns 0 on success.
_Symbol type:_ `function`
#### `fio_fd_type`
```c
inline size_t fio_fd_type(int fd)
```
Returns the file type (or 0 on both error).
See: https://www.man7.org/linux/man-pages/man7/inode.7.html
_Symbol type:_ `function`
#### `fio_filename_is_folder`
```c
#define fio_filename_is_folder(filename) \
(fio_filename_type((filename)) == S_IFDIR)
```
Tests if `filename` references a folder. Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_fd_write`
```c
inline ssize_t fio_fd_write(int fd, const void *buf, size_t len)
```
Writes data to a file handle, returning the number of bytes written.
Returns -1 on error.
Since some systems have a limit on the number of bytes that can be written at
a time, this function fragments the system calls into smaller `write` blocks,
allowing large data to be written.
If the file descriptor is non-blocking, test errno for EAGAIN / EWOULDBLOCK.
_Symbol type:_ `function`
#### `fio_fd_read`
```c
inline size_t fio_fd_read(int fd, void *buf, size_t len, off_t start_at)
```
Reads up to `len` bytes from `fd`, returning the number of bytes read.
Returns 0 if no bytes were read or on error.
Since some systems have a limit on the number of bytes that can be read at
a time, this function fragments the system calls into smaller `read` blocks,
allowing large data to be read.
If the file descriptor is non-blocking, test errno for EAGAIN / EWOULDBLOCK.
_Symbol type:_ `function`
#### `fio_filename_parse`
```c
fio_filename_s fio_filename_parse(const char *filename)
```
Parses a file name to folder, base name and extension (zero-copy).
_Symbol type:_ `function`
#### `fio_filename_parse2`
```c
fio_filename_s fio_filename_parse2(const char *filename, size_t len)
```
Parses a file name to folder, base name and extension (zero-copy).
_Symbol type:_ `function`
#### `fio_fd_find_next`
```c
size_t fio_fd_find_next(int fd, char token, size_t start_at)
```
Returns offset for the next `token` in `fd`, or -1 if reached EOF.
This will use `FIO_FD_FIND_BLOCK` bytes on the stack to read the file in a
loop.
Pros: limits memory use and (re)allocations, easier overflow protection.
Cons: may be slower, as data will most likely be copied again from the file.
_Symbol type:_ `function`
#### `fio_file_dup`
```c
#define fio_file_dup(fd) _dup(fd)
```
Duplicates the file handle (int)
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/004 http1 parser.h`
3 public symbols.
### Macros
#### `FIO_HTTP1_PARSER_INIT`
```c
#define FIO_HTTP1_PARSER_INIT ((fio_http1_parser_s){0})
```
Initialization value for the parser
_Symbol type:_ `macro`
#### `FIO_HTTP1_PARSER_ERROR`
```c
#define FIO_HTTP1_PARSER_ERROR ((size_t)-1)
```
The error return value for fio_http1_parse.
_Symbol type:_ `macro`
#### `FIO_HTTP1_EXPECTED_CHUNKED`
```c
#define FIO_HTTP1_EXPECTED_CHUNKED ((size_t)(-2))
```
A return value for `fio_http1_expected` when chunked data is expected.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/004 json.h`
5 public symbols.
### Macros
#### `FIO_JSON_MAX_DEPTH`
```c
#define FIO_JSON_MAX_DEPTH 128
```
Maximum allowed JSON nesting level. MUST be less then 65536
Values above 65536 might cause the stack to overflow and cause a failure.
_Symbol type:_ `macro`
#### `FIO_JSON_USE_FIO_ATON`
```c
#define FIO_JSON_USE_FIO_ATON 0
```
_Symbol type:_ `macro`
### Types
#### `fio_json_parser_callbacks_s`
```c
typedef struct {
/** NULL object was detected. Returns new object as `void *`. */
void *(*on_null)(void *udata);
/** TRUE object was detected. Returns new object as `void *`. */
void *(*on_true)(void *udata);
/** FALSE object was detected. Returns new object as `void *`. */
void *(*on_false)(void *udata);
/** Number was detected (long long). Returns new object as `void *`. */
void *(*on_number)(void *udata, int64_t i);
/** Float was detected (double).Returns new object as `void *`. */
void *(*on_float)(void *udata, double f);
/** (escaped) String was detected. Returns a new String as `void *`. */
void *(*on_string)(void *udata, const void *start, size_t len);
/** (unescaped) String was detected. Returns a new String as `void *`. */
void *(*on_string_simple)(void *udata, const void *start, size_t len);
/** Dictionary was detected. Returns ctx to hash map or NULL on error. */
void *(*on_map)(void *udata, void *ctx, void *at);
/** Array was detected. Returns ctx to array or NULL on error. */
void *(*on_array)(void *udata, void *ctx, void *at);
/** Map entry detected. Returns non-zero on error. Owns key and value. */
int (*map_push)(void *udata, void *ctx, void *key, void *value);
/** Array entry detected. Returns non-zero on error. Owns value. */
int (*array_push)(void *udata, void *ctx, void *value);
/** Called when an array object (`ctx`) appears done. */
int (*array_finished)(void *udata, void *ctx);
/** Called when a map object (`ctx`) appears done. */
int (*map_finished)(void *udata, void *ctx);
/** Called when context is expected to be an array (i.e., fio_json_update). */
int (*is_array)(void *udata, void *ctx);
/** Called when context is expected to be a map (i.e., fio_json_update). */
int (*is_map)(void *udata, void *ctx);
/** Called for unused objects (e.g., key on error). Must free the object. */
void (*free_unused_object)(void *udata, void *ctx);
/** The JSON parsing encountered an error. Owns ctx, should free or return. */
void *(*on_error)(void *udata, void *ctx);
} fio_json_parser_callbacks_s
```
The JSON parser settings (callbacks).
**Ownership**: Callbacks that return `void *` objects transfer ownership to
the parser. The parser will either pass these objects to `map_push` /
`array_push` (transferring ownership to the container), or call
`free_unused_object` if the object is not used (e.g., on error or NULL map
key). The `on_error` callback receives ownership of any partial result.
_Symbol type:_ `type`
#### `fio_json_result_s`
```c
typedef struct {
void *ctx;
size_t stop_pos;
int err;
} fio_json_result_s
```
The JSON return type.
_Symbol type:_ `type`
### Functions
#### `fio_json_parse`
```c
fio_json_result_s fio_json_parse(fio_json_parser_callbacks_s *settings, void *udata, const char *json_string, const size_t len)
```
The facil.io JSON parser is a non-strict parser, with support for trailing
commas in collections, new-lines in strings, extended escape characters and
octal, hex and binary numbers.
The parser allows for streaming data and decouples the parsing process from
the resulting data-structure by calling static callbacks for JSON related
events.
Returns the number of bytes consumed before parsing stopped (due to either
error or end of data). Stops as close as possible to the end of the buffer or
once an object parsing was completed.
Buffer requirement (guard-byte contract): number / quote-less key scanning
may read the byte at `json_string[len]` while deciding a token ended. The
buffer MUST remain readable through a non-numeric guard byte - pass a
NUL-terminated string (`fio_bstr` qualifies) or append a guard byte.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 multipart.h`
3 public symbols.
### Types
#### `fio_multipart_parser_callbacks_s`
```c
typedef struct {
/**
* Called for each regular form field (no filename).
* Returns user-defined context (can be NULL).
*
* Note: If on_field_start is provided, this callback is ignored and the
* streaming field callbacks (on_field_start/on_field_data/on_field_end)
* are used instead.
*/
void *(*on_field)(void *udata,
fio_buf_info_s name,
fio_buf_info_s value,
fio_buf_info_s content_type);
/**
* Called when a large field starts (optional).
* If NULL but on_field exists, on_field is used instead (backward
* compatible). Returns context for this field (passed to
* on_field_data/on_field_end).
*/
void *(*on_field_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s content_type);
/**
* Called with field data chunk.
* May be called multiple times per field for streaming.
* Returns non-zero to abort parsing.
*/
int (*on_field_data)(void *udata, void *field_ctx, fio_buf_info_s data);
/**
* Called when field ends.
*/
void (*on_field_end)(void *udata, void *field_ctx);
/**
* Called when a file upload starts.
* Returns context for this file (passed to on_file_data/on_file_end).
*/
void *(*on_file_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s filename,
fio_buf_info_s content_type);
/**
* Called with file data chunk.
* May be called multiple times per file for streaming.
* Returns non-zero to abort parsing.
*/
int (*on_file_data)(void *udata, void *file_ctx, fio_buf_info_s data);
/**
* Called when file upload ends.
*/
void (*on_file_end)(void *udata, void *file_ctx);
/**
* Called on parse error (optional).
*/
void (*on_error)(void *udata);
} fio_multipart_parser_callbacks_s
```
The MIME multipart parser callbacks.
_Symbol type:_ `type`
#### `fio_multipart_result_s`
```c
typedef struct {
/** Number of bytes consumed from the input buffer. */
size_t consumed;
/** Number of form fields parsed. */
size_t field_count;
/** Number of files parsed. */
size_t file_count;
/** Error code: 0 = success, -1 = error, -2 = need more data. */
int err;
} fio_multipart_result_s
```
The MIME multipart parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_multipart_parse`
```c
fio_multipart_result_s fio_multipart_parse(const fio_multipart_parser_callbacks_s *callbacks, void *udata, fio_buf_info_s boundary, const char *data, size_t len)
```
Parse MIME multipart data.
`callbacks` contains the callback functions (should be static const).
`udata` is user data passed to all callbacks.
`boundary` is the multipart boundary string (without leading "--").
`data` is the data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `consumed`: Number of bytes consumed from the buffer
- `field_count`: Number of form fields parsed
- `file_count`: Number of files parsed
- `err`: 0 = success, -1 = error, -2 = need more data
For streaming, call again with remaining data appended to unconsumed data.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 resp3.h`
24 public symbols.
### Macros
#### `FIO_RESP3_MAX_NESTING`
```c
#define FIO_RESP3_MAX_NESTING 32
```
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_THRESHOLD`
```c
#define FIO_RESP3_STREAM_THRESHOLD 4096
```
_Symbol type:_ `macro`
#### `FIO_RESP3_SIMPLE_STR`
```c
#define FIO_RESP3_SIMPLE_STR '+'
```
Simple String: `+\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_SIMPLE_ERR`
```c
#define FIO_RESP3_SIMPLE_ERR '-'
```
Simple Error: `-\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_NUMBER`
```c
#define FIO_RESP3_NUMBER ':'
```
Number: `:\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_NULL`
```c
#define FIO_RESP3_NULL '_'
```
Null: `_\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_DOUBLE`
```c
#define FIO_RESP3_DOUBLE ','
```
Double: `,\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BOOL`
```c
#define FIO_RESP3_BOOL '#'
```
Boolean: `#t\r\n` or `#f\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BIGNUM`
```c
#define FIO_RESP3_BIGNUM '('
```
Big Number: `(\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BLOB_STR`
```c
#define FIO_RESP3_BLOB_STR '$'
```
Blob String: `$\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BLOB_ERR`
```c
#define FIO_RESP3_BLOB_ERR '!'
```
Blob Error: `!\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_VERBATIM`
```c
#define FIO_RESP3_VERBATIM '='
```
Verbatim String: `=\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_ARRAY`
```c
#define FIO_RESP3_ARRAY '*'
```
Array: `*\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_MAP`
```c
#define FIO_RESP3_MAP '%'
```
Map: `%\r\n...key-value pairs...`
_Symbol type:_ `macro`
#### `FIO_RESP3_SET`
```c
#define FIO_RESP3_SET '~'
```
Set: `~\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_PUSH`
```c
#define FIO_RESP3_PUSH '>'
```
Push: `>\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_ATTR`
```c
#define FIO_RESP3_ATTR '|'
```
Attribute: `|\r\n...key-value pairs...`
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_CHUNK`
```c
#define FIO_RESP3_STREAM_CHUNK ';'
```
Streamed string chunk: `;`
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_END`
```c
#define FIO_RESP3_STREAM_END '.'
```
Streamed aggregate end: `.`
_Symbol type:_ `macro`
### Types
#### `fio_resp3_frame_s`
```c
typedef struct {
/** Context for this container (returned by on_array/on_map/etc) */
void *ctx;
/** For maps: the pending key waiting for its value */
void *key;
/** Expected remaining elements */
int64_t remaining;
/** Type of this frame */
uint8_t type;
/** Is this a streaming type? */
uint8_t streaming;
/** For maps: are we expecting a key (0) or value (1)? */
uint8_t expecting_value;
/** Set treated as map: need to duplicate values as key+value */
uint8_t set_as_map;
} fio_resp3_frame_s
```
Parser frame for tracking nested structures
_Symbol type:_ `type`
#### `fio_resp3_parser_s`
```c
typedef struct {
/** User data passed to all callbacks */
void *udata;
/** Current nesting depth */
uint32_t depth;
/** Protocol error flag */
uint8_t error;
/** Streaming string in progress flag */
uint8_t streaming_string;
/** Streaming string type (FIO_RESP3_BLOB_STR, FIO_RESP3_BLOB_ERR, etc.) */
uint8_t streaming_string_type;
/** Streamed fixed-length blob: trailing CRLF bytes pending (0...2) */
uint8_t streaming_blob_crlf;
/** Context for streaming string (from on_start_string) */
void *streaming_string_ctx;
/**
* Streamed fixed-length blob: data bytes remaining to stream.
* Zero when inactive or in `$?` (chunked) streaming mode.
*/
int64_t streaming_remaining;
/** Stack for nested structures */
fio_resp3_frame_s stack[FIO_RESP3_MAX_NESTING];
} fio_resp3_parser_s
```
RESP3 parser state
_Symbol type:_ `type`
#### `fio_resp3_callbacks_s`
```c
typedef struct {
/* ===== Primitive Callbacks - return the created object ===== */
/** Called when NULL (`_`) is received. Returns new object. */
void *(*on_null)(void *udata);
/** Called when Boolean (`#t` or `#f`) is received. Returns new object. */
void *(*on_bool)(void *udata, int is_true);
/** Called when a Number (`:`) is parsed. Returns new object. */
void *(*on_number)(void *udata, int64_t num);
/** Called when a Double (`,`) is parsed. Returns new object. */
void *(*on_double)(void *udata, double num);
/** Called when a Big Number (`(`) is parsed. Returns new object. */
void *(*on_bignum)(void *udata, const void *data, size_t len);
/**
* Called when a complete String is received.
* `type` is FIO_RESP3_SIMPLE_STR, FIO_RESP3_BLOB_STR, or FIO_RESP3_VERBATIM.
* Returns new object.
*/
void *(*on_string)(void *udata, const void *data, size_t len, uint8_t type);
/**
* Called when an error message is received (simple `-` or blob `!`).
* `type` is FIO_RESP3_SIMPLE_ERR or FIO_RESP3_BLOB_ERR.
* Returns new object.
*/
void *(*on_error)(void *udata, const void *data, size_t len, uint8_t type);
/* ===== Container Callbacks - receive parent ctx, return new ctx ===== */
/** Called when an Array starts. Returns new array context. */
void *(*on_array)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Map starts. Returns new map context. */
void *(*on_map)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Set starts. Returns new set context. */
void *(*on_set)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Push message starts. Returns new push context. */
void *(*on_push)(void *udata, void *parent_ctx, int64_t len);
/** Called when an Attribute starts. Returns new attribute context. */
void *(*on_attr)(void *udata, void *parent_ctx, int64_t len);
/* ===== Push Callbacks - add child to container ===== */
/** Add value to array. Returns non-zero on error. */
int (*array_push)(void *udata, void *ctx, void *value);
/** Add key-value pair to map. Returns non-zero on error. */
int (*map_push)(void *udata, void *ctx, void *key, void *value);
/** Add value to set. Returns non-zero on error. */
int (*set_push)(void *udata, void *ctx, void *value);
/** Add value to push message. Returns non-zero on error. */
int (*push_push)(void *udata, void *ctx, void *value);
/** Add key-value pair to attribute. Returns non-zero on error. */
int (*attr_push)(void *udata, void *ctx, void *key, void *value);
/* ===== Done Callbacks (optional) - finalize container ===== */
/** Called when array is complete. Returns final object. */
void *(*array_done)(void *udata, void *ctx);
/** Called when map is complete. Returns final object. */
void *(*map_done)(void *udata, void *ctx);
/** Called when set is complete. Returns final object. */
void *(*set_done)(void *udata, void *ctx);
/** Called when push is complete. Returns final object. */
void *(*push_done)(void *udata, void *ctx);
/** Called when attribute is complete. Returns final object. */
void *(*attr_done)(void *udata, void *ctx);
/* ===== Error Handling ===== */
/** Free an unused object (e.g., orphaned key on error). */
void (*free_unused)(void *udata, void *obj);
/** Called on protocol error. */
void *(*on_error_protocol)(void *udata);
/* ===== Streaming String Callbacks (optional) ===== */
/**
* Called when a blob string starts (before data arrives).
* `len` is the declared length of the string ((size_t)-1 for streaming).
* `type` is FIO_RESP3_BLOB_STR, FIO_RESP3_BLOB_ERR, or FIO_RESP3_VERBATIM.
* Returns a context for the string being built (e.g., a string buffer).
* If NULL is returned, falls back to buffering and calling on_string when
* complete.
*/
void *(*on_start_string)(void *udata, size_t len, uint8_t type);
/**
* Called with partial string data (may be called multiple times).
* `ctx` is the context returned by on_start_string.
* Returns 0 on success, non-zero to abort parsing.
*/
int (*on_string_write)(void *udata, void *ctx, const void *data, size_t len);
/**
* Called when the string is complete.
* `ctx` is the context returned by on_start_string.
* Returns the final string object to be used as a value.
*/
void *(*on_string_done)(void *udata, void *ctx, uint8_t type);
} fio_resp3_callbacks_s
```
The RESP3 parser callbacks (designed to be static const).
All callbacks receive `udata` from the parser state as their first argument.
_Symbol type:_ `type`
#### `fio_resp3_result_s`
```c
typedef struct {
/** The parsed top-level object (or NULL on error/incomplete) */
void *obj;
/** Number of bytes consumed from the buffer */
size_t consumed;
/** Non-zero if an error occurred */
int err;
} fio_resp3_result_s
```
The RESP3 parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_resp3_parse`
```c
fio_resp3_result_s fio_resp3_parse(fio_resp3_parser_s *parser, const fio_resp3_callbacks_s *callbacks, const void *buf, size_t len)
```
Parse RESP3 data from buffer.
`parser` is the parser state. Initialize with `{.udata = my_data}` for first
call. For continuation after partial parse, pass the same parser.
`callbacks` contains the callback functions (should be static const).
`buf` is the data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `obj`: The parsed top-level object (NULL if incomplete or error)
- `consumed`: Number of bytes consumed from the buffer
- `err`: Non-zero if a protocol error occurred
For partial data, the parser state is preserved. Call again with remaining
data appended to unconsumed data.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 sock.h`
35 public symbols.
### Macros
#### `FIO_SOCKET_INVALID`
```c
#define FIO_SOCKET_INVALID INVALID_SOCKET
```
Sentinel value for an invalid socket handle.
_Symbol type:_ `macro`
#### `FIO_SOCK_FD_ISVALID`
```c
#define FIO_SOCK_FD_ISVALID(fd) ((fio_socket_i)(fd) != FIO_SOCKET_INVALID)
```
_Symbol type:_ `macro`
#### `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`
```c
#define FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT (1ULL << 24)
```
_Symbol type:_ `macro`
#### `FIO_SOCK_UNIX`
```c
#define FIO_SOCK_UNIX 0
```
_Symbol type:_ `macro`
#### `FIO_SOCK_UNIX_PRIVATE`
```c
#define FIO_SOCK_UNIX_PRIVATE 0
```
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_RW`
```c
#define FIO_SOCK_WAIT_RW(fd, timeout_) \
fio_sock_wait_io(fd, POLLIN | POLLOUT, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_R`
```c
#define FIO_SOCK_WAIT_R(fd, timeout_) fio_sock_wait_io(fd, POLLIN, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_W`
```c
#define FIO_SOCK_WAIT_W(fd, timeout_) fio_sock_wait_io(fd, POLLOUT, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_IS_OPEN`
```c
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, (POLLOUT | POLLRDHUP), 0) & \
(POLLRDHUP | POLLHUP | POLLNVAL)))
```
A helper macro that tests if a socket was closed.
_Symbol type:_ `macro`
### Types
#### `fio_socket_i`
```c
typedef int fio_socket_i
```
Native socket handle type: int on POSIX.
_Symbol type:_ `type`
#### `fio_sock_open_flags_e`
```c
typedef enum {
FIO_SOCK_SERVER = 0,
FIO_SOCK_CLIENT = 1,
FIO_SOCK_NONBLOCK = 2,
FIO_SOCK_TCP = 4,
FIO_SOCK_UDP = 8,
#ifdef AF_UNIX
FIO_SOCK_UNIX = 16,
FIO_SOCK_UNIX_PRIVATE = (16 | 32),
#else
#define FIO_SOCK_UNIX 0
#define FIO_SOCK_UNIX_PRIVATE 0
#endif
} fio_sock_open_flags_e
```
Socket type flags
_Symbol type:_ `type`
### Functions
#### `fio_sock_write`
```c
inline ssize_t fio_sock_write(fio_socket_i fd, const void *buf, size_t len)
```
Acts as POSIX write. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_read`
```c
inline ssize_t fio_sock_read(fio_socket_i fd, void *buf, size_t len)
```
Acts as POSIX read. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_sendto`
```c
inline ssize_t fio_sock_sendto(fio_socket_i fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen)
```
Acts as POSIX sendto. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_recvfrom`
```c
inline ssize_t fio_sock_recvfrom(fio_socket_i fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen)
```
Acts as POSIX recvfrom. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_dup`
```c
inline fio_socket_i fio_sock_dup(fio_socket_i fd)
```
Acts as POSIX dup. Sets O_CLOEXEC on the new fd.
_Symbol type:_ `function`
#### `fio_sock_close`
```c
inline int fio_sock_close(fio_socket_i fd)
```
Acts as POSIX close. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_accept`
```c
#define fio_sock_accept(fd, addr, addrlen) accept(fd, addr, addrlen)
```
Acts as POSIX accept. Use this macro for portability with WinSock2.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sock_bind`
```c
inline int fio_sock_bind(fio_socket_i fd, const struct sockaddr *addr, socklen_t addrlen)
```
Portable bind. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_connect`
```c
inline int fio_sock_connect(fio_socket_i fd, const struct sockaddr *addr, socklen_t addrlen)
```
Portable connect. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_listen`
```c
inline int fio_sock_listen(fio_socket_i fd, int backlog)
```
Portable listen. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_setsockopt`
```c
inline int fio_sock_setsockopt(fio_socket_i fd, int level, int optname, const void *optval, socklen_t optlen)
```
Portable setsockopt. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_socketpair`
```c
inline int fio_sock_socketpair(fio_socket_i fds[2])
```
Creates a connected socket pair using POSIX socketpair().
_Symbol type:_ `function`
#### `fio_sock_pipe`
```c
inline int fio_sock_pipe(fio_socket_i fds[2])
```
Creates a POSIX pipe. fds[0] = read end, fds[1] = write end.
On Windows, fio_sock_pipe() is defined in the FIO_OS_WIN block above and
delegates to fio_sock_socketpair() (loopback TCP).
_Symbol type:_ `function`
#### `fio_sock_open`
```c
inline fio_socket_i fio_sock_open(const char *restrict address, const char *restrict port, uint16_t flags)
```
Creates a new socket according to the provided flags.
The `port` string will be ignored when `FIO_SOCK_UNIX` is set.
_Symbol type:_ `function`
#### `fio_sock_open2`
```c
fio_socket_i fio_sock_open2(const char *url, uint16_t flags)
```
Creates a new socket, according to the provided flags.
_Symbol type:_ `function`
#### `fio_sock_address_new`
```c
inline struct addrinfo *fio_sock_address_new(const char *restrict address, const char *restrict port, int sock_type)
```
Attempts to resolve an address to a valid IP6 / IP4 address pointer.
The `sock_type` element should be a socket type, such as `SOCK_DGRAM` (UDP)
or `SOCK_STREAM` (TCP/IP).
The address should be freed using `fio_sock_address_free`.
_Symbol type:_ `function`
#### `fio_sock_address_free`
```c
inline void fio_sock_address_free(struct addrinfo *a)
```
Frees the pointer returned by `fio_sock_address_new`.
_Symbol type:_ `function`
#### `fio_sock_peer_addr`
```c
fio_buf_info_s fio_sock_peer_addr(fio_socket_i s)
```
Returns a human readable address representation of the socket's peer address.
On error, returns a NULL buffer with zero length.
Buffer lengths are limited to 63 bytes.
This function is limited in its thread safety to 128 threads / calls.
_Symbol type:_ `function`
#### `fio_sock_open_local`
```c
fio_socket_i fio_sock_open_local(struct addrinfo *addr, int nonblock)
```
Creates a new network socket and binds it to a local address.
_Symbol type:_ `function`
#### `fio_sock_open_remote`
```c
fio_socket_i fio_sock_open_remote(struct addrinfo *addr, int nonblock)
```
Creates a new network socket and connects it to a remote address.
_Symbol type:_ `function`
#### `fio_sock_open_unix`
```c
fio_socket_i fio_sock_open_unix(const char *address, uint16_t flags)
```
Creates a new Unix socket and binds it to a local address.
_Symbol type:_ `function`
#### `fio_sock_set_non_block`
```c
int fio_sock_set_non_block(fio_socket_i fd)
```
Sets a file descriptor / socket to non blocking state.
_Symbol type:_ `function`
#### `fio_sock_maximize_limits`
```c
size_t fio_sock_maximize_limits(size_t maximum_limit)
```
Attempts to maximize the allowed open file limits. returns known limit
_Symbol type:_ `function`
#### `fio_sock_wait_io`
```c
short fio_sock_wait_io(fio_socket_i fd, short events, int timeout)
```
Returns 0 on timeout, -1 on error or the events that are valid.
A zero timeout returns immediately.
Possible events include POLLIN | POLLOUT
Possible return values include POLLIN | POLLOUT | POLLHUP | POLLNVAL
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 state callbacks.h`
4 public symbols.
### Types
#### `fio_state_event_type_e`
```c
typedef enum {
/** Called once during library initialization. */
FIO_CALL_ON_INITIALIZE,
/** Called once before starting up the IO reactor. */
FIO_CALL_PRE_START,
/** Called before forking starts for a worker group. */
FIO_CALL_BEFORE_FORK,
/** Called by a child worker process right after it was forked. */
FIO_CALL_IN_CHILD,
/** Called by the master process right after spawning a worker. */
FIO_CALL_IN_MASTER,
/** Called after forking ends for a worker group. */
FIO_CALL_AFTER_FORK,
/** Called by each worker thread in a Server Async queue as it starts. */
FIO_CALL_ON_WORKER_THREAD_START,
/** Called every time a *Worker* process starts. */
FIO_CALL_ON_START,
/** Reserved for internal use. */
FIO_CALL_RESERVED1,
/** Reserved for internal use. */
FIO_CALL_RESERVED2,
/** User state event queue (unused, available for the user). */
FIO_CALL_ON_USER1,
/** User state event queue (unused, available for the user). */
FIO_CALL_ON_USER2,
/** Called when facil.io enters idling mode. */
FIO_CALL_ON_IDLE,
/** A reversed user state event queue (unused, available for the user). */
FIO_CALL_ON_USER1_REVERSE,
/** A reversed user state event queue (unused, available for the user). */
FIO_CALL_ON_USER2_REVERSE,
/** Reserved for internal use. */
FIO_CALL_RESERVED1_REVERSED,
/** Reserved for internal use. */
FIO_CALL_RESERVED2_REVERSED,
/** Called before starting the shutdown sequence. */
FIO_CALL_ON_SHUTDOWN,
/** Called by each worker the moment it detects the master process crashed. */
FIO_CALL_ON_PARENT_CRUSH,
/** Called by the parent (master) after a worker process crashed. */
FIO_CALL_ON_CHILD_CRUSH,
/** Called by each worker thread in a Server Async queue as it ends. */
FIO_CALL_ON_WORKER_THREAD_END,
/** Called when wither a *Worker* or *Master* stopped. */
FIO_CALL_ON_STOP,
/** An alternative to the system's at_exit. */
FIO_CALL_AT_EXIT,
/** Use after cleanup (e.g., leak testing). */
FIO_CALL_AFTER_EXIT,
/** used for testing and array allocation - must be last. */
FIO_CALL_NEVER
} fio_state_event_type_e
```
a callback type enum
_Symbol type:_ `type`
### Functions
#### `fio_state_callback_add`
```c
void fio_state_callback_add(fio_state_event_type_e, void (*func)(void *), void *arg)
```
Adds a callback to the list of callbacks to be called for the event.
_Symbol type:_ `function`
#### `fio_state_callback_remove`
```c
int fio_state_callback_remove(fio_state_event_type_e, void (*func)(void *), void *arg)
```
Removes a callback from the list of callbacks to be called for the event.
_Symbol type:_ `function`
#### `fio_state_callback_force`
```c
void fio_state_callback_force(fio_state_event_type_e)
```
Forces all the existing callbacks to run, as if the event occurred.
Callbacks for all initialization / idling tasks are called in order of
creation (where fio_state_event_type_e <= FIO_CALL_ON_IDLE).
Callbacks for all cleanup oriented tasks are called in reverse order of
creation (where fio_state_event_type_e >= FIO_CALL_ON_SHUTDOWN).
During an event, changes to the callback list are ignored (callbacks can't
add or remove other callbacks for the same event).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 time.h`
17 public symbols.
### Functions
#### `fio_time_real`
```c
inline struct timespec fio_time_real(void)
```
Returns human (watch) time... this value isn't as safe for measurements.
_Symbol type:_ `function`
#### `fio_time_mono`
```c
inline struct timespec fio_time_mono(void)
```
Returns monotonic time.
_Symbol type:_ `function`
#### `fio_time_nano`
```c
inline int64_t fio_time_nano(void)
```
Returns monotonic time in nano-seconds (now in 1 billionth of a second).
_Symbol type:_ `function`
#### `fio_time_micro`
```c
inline int64_t fio_time_micro(void)
```
Returns monotonic time in micro-seconds (now in 1 millionth of a second).
_Symbol type:_ `function`
#### `fio_time_milli`
```c
inline int64_t fio_time_milli(void)
```
Returns monotonic time in milliseconds.
_Symbol type:_ `function`
#### `fio_time2milli`
```c
inline int64_t fio_time2milli(struct timespec)
```
Converts a `struct timespec` to milliseconds.
_Symbol type:_ `function`
#### `fio_time2micro`
```c
inline int64_t fio_time2micro(struct timespec)
```
Converts a `struct timespec` to microseconds.
_Symbol type:_ `function`
#### `fio_time2gm`
```c
struct tm fio_time2gm(time_t time)
```
A faster (yet less localized) alternative to `gmtime_r`.
See the libc `gmtime_r` documentation for details.
Falls back to `gmtime_r` for dates before epoch.
_Symbol type:_ `function`
#### `fio_gm2time`
```c
time_t fio_gm2time(struct tm tm)
```
Converts a `struct tm` to time in seconds (assuming UTC).
_Symbol type:_ `function`
#### `fio_time2rfc7231`
```c
size_t fio_time2rfc7231(char *target, time_t time)
```
Writes an RFC 7231 date representation (HTTP date format) to target.
Usually requires 29 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2rfc2109`
```c
size_t fio_time2rfc2109(char *target, time_t time)
```
Writes an RFC 2109 date representation to target (HTTP Cookie Format).
Usually requires 31 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2rfc2822`
```c
size_t fio_time2rfc2822(char *target, time_t time)
```
Writes an RFC 2822 date representation to target (Internet Message Format).
Usually requires 28 to 29 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2log`
```c
size_t fio_time2log(char *target, time_t time)
```
Writes a date representation to target in common log format. i.e.,
[DD/MMM/yyyy:hh:mm:ss +0000]
Usually requires 29 characters (including square brackets and NUL).
_Symbol type:_ `function`
#### `fio_time2iso`
```c
size_t fio_time2iso(char *target, time_t time)
```
Writes a date representation to target in ISO 8601 format. i.e.,
YYYY-MM-DD HH:MM:SS
Usually requires 20 characters (including NUL).
_Symbol type:_ `function`
#### `fio_time_add`
```c
inline struct timespec fio_time_add(struct timespec t, struct timespec t2)
```
Adds two `struct timespec` objects.
_Symbol type:_ `function`
#### `fio_time_add_milli`
```c
inline struct timespec fio_time_add_milli(struct timespec t, int64_t milli)
```
Adds milliseconds to a `struct timespec` object.
_Symbol type:_ `function`
#### `fio_time_cmp`
```c
inline int fio_time_cmp(struct timespec t1, struct timespec t2)
```
Compares two `struct timespec` objects.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 urlencoded.h`
3 public symbols.
### Types
#### `fio_url_encoded_parser_callbacks_s`
```c
typedef struct {
/**
* Called for each name=value pair found.
*
* `name` and `value` point directly into the original input data.
* The data is NOT decoded - caller must decode if needed.
*
* Returns the (possibly updated) udata, or NULL to stop parsing.
*/
void *(*on_pair)(void *udata, fio_buf_info_s name, fio_buf_info_s value);
/**
* Called on parsing error (optional).
*
* Currently not used since URL-encoded parsing is very permissive,
* but reserved for future use.
*/
void (*on_error)(void *udata);
} fio_url_encoded_parser_callbacks_s
```
The URL-encoded parser callbacks.
Callbacks receive `udata` as their first argument.
_Symbol type:_ `type`
#### `fio_url_encoded_result_s`
```c
typedef struct {
/** Number of bytes consumed from the buffer. */
size_t consumed;
/** Number of name=value pairs found. */
size_t count;
/** Non-zero if an error occurred (callback returned NULL). */
int err;
} fio_url_encoded_result_s
```
The URL-encoded parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_url_encoded_parse`
```c
fio_url_encoded_result_s fio_url_encoded_parse(const fio_url_encoded_parser_callbacks_s *callbacks, void *udata, const char *data, size_t len)
```
Parse URL-encoded data from buffer.
`callbacks` contains the callback functions (should be static const).
`udata` is user data passed to callbacks.
`data` is the URL-encoded data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `consumed`: Number of bytes consumed from the buffer
- `count`: Number of name=value pairs found
- `err`: Non-zero if parsing was stopped (callback returned NULL)
Parsing rules:
- Pairs are separated by `&`
- Name and value are separated by `=`
- Empty value is valid: `name=` → value.len = 0
- Missing `=` means value is empty: `name` → name="name", value.len = 0
- Empty name with value: `=value` → name.len = 0, value="value"
- Empty pairs (`&&`) are skipped
Note: The parser does NOT decode percent-encoded characters.
Use `fio_string_write_url_dec` to decode if needed.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 websocket parser.h`
29 public symbols.
### Macros
#### `FIO_WEBSOCKET_DEFAULT_MAX_FRAME`
```c
#define FIO_WEBSOCKET_DEFAULT_MAX_FRAME (1ULL << 30)
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_PARSE_ERROR`
```c
#define FIO_WEBSOCKET_PARSE_ERROR ((size_t)-1)
```
Parse error sentinel.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV1`
```c
#define FIO_WEBSOCKET_RSV1 0x4U /* byte-0 bit 6 (permessage-deflate) */
```
RSV bit constants for the send-side API. These are the 3-bit values shifted
into byte-0 bits 4..6 on the wire (0x4 → RSV1=0x40, 0x2 → RSV2=0x20, 0x1 →
RSV3=0x10).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV2`
```c
#define FIO_WEBSOCKET_RSV2 0x2U /* byte-0 bit 5 */
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV3`
```c
#define FIO_WEBSOCKET_RSV3 0x1U /* byte-0 bit 4 */
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_CONT`
```c
#define FIO_WEBSOCKET_OP_CONT 0x0
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_TEXT`
```c
#define FIO_WEBSOCKET_OP_TEXT 0x1
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_BINARY`
```c
#define FIO_WEBSOCKET_OP_BINARY 0x2
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_CLOSE`
```c
#define FIO_WEBSOCKET_OP_CLOSE 0x8
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_PING`
```c
#define FIO_WEBSOCKET_OP_PING 0x9
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_PONG`
```c
#define FIO_WEBSOCKET_OP_PONG 0xA
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_HEADER`
```c
#define FIO_WEBSOCKET_STATE_HEADER 0
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_PAYLOAD`
```c
#define FIO_WEBSOCKET_STATE_PAYLOAD 1
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_CLOSED`
```c
#define FIO_WEBSOCKET_STATE_CLOSED 0xFE
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_ERROR`
```c
#define FIO_WEBSOCKET_STATE_ERROR 0xFF
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_FIN`
```c
#define FIO_WEBSOCKET_FLAG_FIN 0x80
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_MASKED`
```c
#define FIO_WEBSOCKET_FLAG_MASKED 0x40
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_OPCODE_MASK`
```c
#define FIO_WEBSOCKET_FLAG_OPCODE_MASK 0x3C
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_OPCODE_SHIFT`
```c
#define FIO_WEBSOCKET_FLAG_OPCODE_SHIFT 2
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK`
```c
#define FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK 0x03
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_PAUSED`
```c
#define FIO_WEBSOCKET_FLAG2_PAUSED 0x80
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK`
```c
#define FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK 0x70
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT`
```c
#define FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT 4
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_FIN`
```c
#define FIO_WEBSOCKET_GET_FIN(p) (((p)->flags & FIO_WEBSOCKET_FLAG_FIN) != 0)
```
Read the FIN bit from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MASKED`
```c
#define FIO_WEBSOCKET_GET_MASKED(p) \
(((p)->flags & FIO_WEBSOCKET_FLAG_MASKED) != 0)
```
Read the MASK bit from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_OPCODE`
```c
#define FIO_WEBSOCKET_GET_OPCODE(p) \
(((p)->flags & FIO_WEBSOCKET_FLAG_OPCODE_MASK) >> \
FIO_WEBSOCKET_FLAG_OPCODE_SHIFT)
```
Read the opcode from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MSG_OPCODE`
```c
#define FIO_WEBSOCKET_GET_MSG_OPCODE(p) \
((p)->flags & FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK)
```
Read the message opcode (1=text, 2=binary, 0=none open).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_PAUSED`
```c
#define FIO_WEBSOCKET_GET_PAUSED(p) \
(((p)->flags2 & FIO_WEBSOCKET_FLAG2_PAUSED) != 0)
```
Read the paused flag (one-message-per-parse gate).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MSG_RSV`
```c
#define FIO_WEBSOCKET_GET_MSG_RSV(p) \
(((p)->flags2 & FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK) >> \
FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT)
```
Read the RSV bits from the opening frame (3-bit format).
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/005 cli.h`
23 public symbols.
### Macros
#### `FIO_CLI_ARG_NONE`
```c
#define FIO_CLI_ARG_NONE FIO_CLI_ARG_PRINT_HEADER
```
_Symbol type:_ `macro`
#### `FIO_CLI_STRING`
```c
#define FIO_CLI_STRING(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_STRING, .l = line})
```
Indicates the CLI argument should be a String (default).
_Symbol type:_ `macro`
#### `FIO_CLI_BOOL`
```c
#define FIO_CLI_BOOL(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_BOOL, .l = line})
```
Indicates the CLI argument is a Boolean value.
_Symbol type:_ `macro`
#### `FIO_CLI_INT`
```c
#define FIO_CLI_INT(line) ((fio___cli_line_s){.t = FIO_CLI_ARG_INT, .l = line})
```
Indicates the CLI argument should be an Integer (numerical).
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT`
```c
#define FIO_CLI_PRINT(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT, .l = line})
```
Indicates the CLI string should be printed as is with proper offset.
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT_LINE`
```c
#define FIO_CLI_PRINT_LINE(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT_LINE, .l = line})
```
Indicates the CLI string should be printed as is with no offset.
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT_HEADER`
```c
#define FIO_CLI_PRINT_HEADER(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT_HEADER, .l = line})
```
Indicates the CLI string should be printed as a header.
_Symbol type:_ `macro`
### Types
#### `fio_cli_arg_e`
```c
typedef enum {
/** A String CLI argument */
FIO_CLI_ARG_STRING,
/** A Boolean CLI argument */
FIO_CLI_ARG_BOOL,
/** An integer CLI argument */
FIO_CLI_ARG_INT,
FIO_CLI_ARG_PRINT,
FIO_CLI_ARG_PRINT_LINE,
FIO_CLI_ARG_PRINT_HEADER,
} fio_cli_arg_e
```
Used internally.
_Symbol type:_ `type`
### Functions
#### `fio_cli_start`
```c
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
fio_cli_start((argc), \
(argv), \
(unnamed_min), \
(unnamed_max), \
(description), \
(fio___cli_line_s[]){__VA_ARGS__, {0}})
```
This function parses the Command Line Interface (CLI), creating a temporary
"dictionary" that allows easy access to the CLI using their names or aliases.
Command line arguments may be typed. If an optional type requirement is
provided and the provided arument fails to match the required type, execution
will end and an error message will be printed along with a short "help".
The function / macro accepts the following arguments:
- `argc`: command line argument count.
- `argv`: command line argument list (array).
- `unnamed_min`: the required minimum of un-named arguments.
- `unnamed_max`: the maximum limit of un-named arguments.
- `description`: a C string containing the program's description.
- named arguments list: a list of C strings describing named arguments.
The following optional type requirements are:
* FIO_CLI_STRING(desc_line) - (default) string argument.
* FIO_CLI_BOOL(desc_line) - boolean argument (no value).
* FIO_CLI_INT(desc_line) - integer argument.
* FIO_CLI_PRINT_HEADER(desc_line) - extra header for output.
* FIO_CLI_PRINT(desc_line) - extra information for output.
Argument names MUST start with the '-' character. The first word starting
without the '-' character will begin the description for the CLI argument.
The arguments "-?", "-h", "-help" and "--help" are automatically handled
unless overridden.
Un-named arguments shouldn't be listed in the named arguments list.
Example use:
fio_cli_start(argc, argv, 0, 0, "The NAME example accepts the following:",
FIO_CLI_PRINT_HREADER("Concurrency:"),
FIO_CLI_INT("-t -thread number of threads to run."),
FIO_CLI_INT("-w -workers number of workers to run."),
FIO_CLI_PRINT_HREADER("Address Binding:"),
"-b, -address the address to bind to.",
FIO_CLI_INT("-p,-port the port to bind to."),
FIO_CLI_PRINT("\t\tset port to zero (0) for Unix s."),
FIO_CLI_PRINT_HREADER("Logging:"),
FIO_CLI_BOOL("-v -log enable logging.")
);
This would allow access to the named arguments:
fio_cli_get("-b") == fio_cli_get("-address");
Once all the data was accessed, free the parsed data dictionary using:
fio_cli_end();
It should be noted, arguments will be recognized in a number of forms, i.e.:
app -t=1 -p3000 -a localhost
This function is NOT thread safe.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_cli_start`
```c
void fio_cli_start FIO_NOOP(int argc, char const *argv[], int unnamed_min, int unnamed_max, char const *description, fio___cli_line_s *arguments)
```
Never use the function directly, always use the MACRO, because the macro
attaches a NULL marker at the end of the `names` argument collection.
_Symbol type:_ `function`
#### `fio_cli_end`
```c
void fio_cli_end(void)
```
Clears the memory used by the CLI dictionary, removing all parsed data.
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_get`
```c
char const *fio_cli_get(char const *name)
```
Returns the argument's value as a NUL terminated C String.
_Symbol type:_ `function`
#### `fio_cli_get_str`
```c
fio_buf_info_s fio_cli_get_str(char const *name)
```
Returns the argument's value as a NUL terminated `fio_buf_info_s`.
_Symbol type:_ `function`
#### `fio_cli_get_i`
```c
int64_t fio_cli_get_i(char const *name)
```
Returns the argument's value as an integer.
_Symbol type:_ `function`
#### `fio_cli_get_bool`
```c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
```
This MACRO returns the argument's value as a boolean.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_cli_unnamed_count`
```c
unsigned int fio_cli_unnamed_count(void)
```
Returns the number of unnamed argument.
_Symbol type:_ `function`
#### `fio_cli_unnamed`
```c
char const *fio_cli_unnamed(unsigned int index)
```
Returns the unnamed argument using a 0 based `index`.
_Symbol type:_ `function`
#### `fio_cli_unnamed_str`
```c
fio_buf_info_s fio_cli_unnamed_str(unsigned int index)
```
Returns the unnamed argument using a 0 based `index`.
_Symbol type:_ `function`
#### `fio_cli_set`
```c
void fio_cli_set(char const *name, char const *value)
```
Sets the argument's value as a NUL terminated C String.
fio_cli_set("-p", "hello");
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_set_i`
```c
void fio_cli_set_i(char const *name, int64_t i)
```
Sets the argument's value as a NUL terminated C String.
fio_cli_start(argc, argv,
"this is example accepts the following options:",
"-p -port the port to bind to", FIO_CLI_INT;
fio_cli_set("-p", "hello"); // fio_cli_get("-p") == fio_cli_get("-port");
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_set_unnamed`
```c
unsigned int fio_cli_set_unnamed(unsigned int index, const char *)
```
Sets / adds an unnamed argument to the 0 based array of unnamed elements.
_Symbol type:_ `function`
#### `fio_cli_each`
```c
size_t fio_cli_each(int (*task)(fio_buf_info_s name, fio_buf_info_s value, fio_cli_arg_e arg_type, void *udata), void *udata)
```
Calls `task` for every argument received.
_Symbol type:_ `function`
#### `fio_cli_type`
```c
fio_cli_arg_e fio_cli_type(char const *name)
```
Returns the argument's expected content type.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/010 mem.h`
50 public symbols.
### Macros
#### `FIO_MEMORY_ALIGN_LOG`
```c
#define FIO_MEMORY_ALIGN_LOG 6
```
Allocation alignment, MUST be >= 3 and <= 10
_Symbol type:_ `macro`
#### `FIO_MEMORY_ALIGN_SIZE`
```c
#define FIO_MEMORY_ALIGN_SIZE (1UL << (FIO_MEMORY_ALIGN_LOG))
```
The minimal allocation size & alignment.
_Symbol type:_ `macro`
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG 21
```
The logarithmic size of a single allocation "chunk" (16 blocks).
Limited to >=17 and <=24.
By default 21, which is a ~2Mb allocation per system call, resulting in a
maximum allocation size of 64Kb.
_Symbol type:_ `macro`
#### `FIO_MEMORY_CACHE_SLOTS`
```c
#define FIO_MEMORY_CACHE_SLOTS 4
```
The number of system allocation "chunks" to cache even if they are not in
use.
_Symbol type:_ `macro`
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS \
FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT
```
Forces the allocator to zero out memory early and often, so allocations
return initialized memory (bytes are all zeros).
This will make the realloc2 safe for use (all data not copied is zero).
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG 2
```
The number of blocks per system allocation.
More blocks protect against fragmentation, but lower the maximum number that
can be allocated without reverting to mmap.
Range: 0-4
Recommended: depends on object allocation sizes, usually 1 or 2.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ENABLE_BIG_ALLOC`
```c
#define FIO_MEMORY_ENABLE_BIG_ALLOC 1
```
Uses a whole system allocation to support bigger allocations.
Could increase fragmentation costs.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT`
```c
#define FIO_MEMORY_ARENA_COUNT -1
```
Memory arenas mitigate thread contention while using more memory.
Note that at some point arenas are statistically irrelevant... except when
benchmarking contention in multi-core machines.
Negative values will result in dynamic selection based on CPU core count.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT_FALLBACK`
```c
#define FIO_MEMORY_ARENA_COUNT_FALLBACK 24
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT_MAX`
```c
#define FIO_MEMORY_ARENA_COUNT_MAX 64
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_USE_THREAD_MUTEX`
```c
#define FIO_MEMORY_USE_THREAD_MUTEX 1
```
If arena count isn't linked to the CPU count, threads might busy-spin.
It is better to slow wait than fast busy spin when the work in the lock is
longer... and system allocations are performed inside arena locks.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION \
(1UL << FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG)
```
the number of allocation blocks per system allocation.
_Symbol type:_ `macro`
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE \
(1UL << FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG)
```
the total number of bytes consumed per system allocation.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCK_ALLOC_LIMIT`
```c
#define FIO_MEMORY_BLOCK_ALLOC_LIMIT \
(FIO_MEMORY_SYS_ALLOCATION_SIZE >> (FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG + 2))
```
The maximum allocation size, after which a big/system allocation is used.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_ALLOC_LIMIT`
```c
#define FIO_MEMORY_BIG_ALLOC_LIMIT \
(FIO_MEMORY_SYS_ALLOCATION_SIZE >> \
(FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG > 3 \
? 3 \
: FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG))
```
the limit of a big allocation, if enabled
_Symbol type:_ `macro`
#### `FIO_MEMORY_ALLOC_LIMIT`
```c
#define FIO_MEMORY_ALLOC_LIMIT FIO_MEMORY_BIG_ALLOC_LIMIT
```
_Symbol type:_ `macro`
#### `FIO_MEM_BYTES2PAGES`
```c
#define FIO_MEM_BYTES2PAGES(size) \
((size > (SIZE_MAX - ((1UL << FIO_MEM_PAGE_SIZE_LOG) - 1))) \
? size \
: (((size_t)(size) + ((1UL << FIO_MEM_PAGE_SIZE_LOG) - 1)) & \
((~(size_t)0) << FIO_MEM_PAGE_SIZE_LOG)))
```
_Symbol type:_ `macro`
#### `FIO_PAGE_ALIGN`
```c
#define FIO_PAGE_ALIGN \
__attribute__((assume_aligned((1UL << FIO_MEM_PAGE_SIZE_LOG))))
```
_Symbol type:_ `macro`
#### `FIO_PAGE_ALIGN_NEW`
```c
#define FIO_PAGE_ALIGN_NEW \
__attribute__((malloc, assume_aligned((1UL << FIO_MEM_PAGE_SIZE_LOG))))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_HEADER_SIZE`
```c
#define FIO_MEMORY_HEADER_SIZE \
((sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_chunk_s)) + \
(FIO_MEMORY_ALIGN_SIZE - 1)) & \
(~(FIO_MEMORY_ALIGN_SIZE - 1)))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_UNITS_PER_BLOCK`
```c
#define FIO_MEMORY_UNITS_PER_BLOCK \
(FIO_MEMORY_BLOCK_SIZE / FIO_MEMORY_ALIGN_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_STATE_SIZE`
```c
#define FIO_MEMORY_STATE_SIZE(arena_count) \
FIO_MEM_BYTES2PAGES( \
(sizeof(*FIO_NAME(FIO_MEMORY_NAME, __mem_state)) + \
(sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_arena_s)) * (arena_count))))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_MARKER`
```c
#define FIO_MEMORY_BIG_BLOCK_MARKER ((~(uint32_t)0) << 2)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_HEADER_SIZE`
```c
#define FIO_MEMORY_BIG_BLOCK_HEADER_SIZE \
(((sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_big_block_s)) + \
((FIO_MEMORY_ALIGN_SIZE - 1))) & \
((~(0UL)) << FIO_MEMORY_ALIGN_LOG)))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_SIZE`
```c
#define FIO_MEMORY_BIG_BLOCK_SIZE \
(FIO_MEMORY_SYS_ALLOCATION_SIZE - FIO_MEMORY_BIG_BLOCK_HEADER_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_UNITS_PER_BIG_BLOCK`
```c
#define FIO_MEMORY_UNITS_PER_BIG_BLOCK \
(FIO_MEMORY_BIG_BLOCK_SIZE / FIO_MEMORY_ALIGN_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_TEST_MULTI_THREADED`
```c
#define FIO_TEST_MULTI_THREADED 0
```
_Symbol type:_ `macro`
### Functions
#### `fio_malloc`
```c
void *FIO_MEM_ALIGN_NEW fio_malloc(size_t size)
```
Allocates memory using a per-CPU core block memory pool.
Memory is zeroed out.
Allocations above FIO_MEMORY_BLOCK_ALLOC_LIMIT will be redirected to `mmap`,
as if `mempool_mmap` was called.
`mempool_malloc` promises a best attempt at providing locality between
consecutive calls, but locality can't be guaranteed.
_Symbol type:_ `function`
#### `fio_calloc`
```c
void *FIO_MEM_ALIGN_NEW fio_calloc(size_t size_per_unit, size_t unit_count)
```
same as calling `fio_malloc(size_per_unit * unit_count)`;
Allocations above FIO_MEMORY_BLOCK_ALLOC_LIMIT will be redirected to `mmap`,
as if `mempool_mmap` was called.
_Symbol type:_ `function`
#### `fio_free`
```c
void fio_free(void *ptr)
```
Frees memory that was allocated using this library.
_Symbol type:_ `function`
#### `fio_realloc`
```c
void *FIO_MEM_ALIGN fio_realloc(void *ptr, size_t new_size)
```
Re-allocates memory. An attempt to avoid copying the data is made only for
big memory allocations (larger than FIO_MEMORY_BLOCK_ALLOC_LIMIT).
_Symbol type:_ `function`
#### `fio_realloc2`
```c
void *FIO_MEM_ALIGN fio_realloc2(void *ptr, size_t new_size, size_t copy_len)
```
Re-allocates memory. An attempt to avoid copying the data is made only for
big memory allocations (larger than FIO_MEMORY_BLOCK_ALLOC_LIMIT).
This variation can perform better, as it might copy less data.
_Symbol type:_ `function`
#### `fio_realloc_aligned`
```c
void *FIO_MEM_ALIGN fio_realloc_aligned(void *ptr, size_t new_size, size_t copy_len, size_t alignment)
```
Re-allocates memory, enforcing a minimum pointer alignment.
This is the core of the aligned allocation API: `malloc_aligned` and
`calloc_aligned` simply route to this function with a NULL `ptr`.
The `alignment` argument is normalized as follows:
- `0` is treated as the allocator's default (`FIO_MEMORY_ALIGN_SIZE`);
- non power-of-2 values are rounded DOWN to the nearest power of 2;
- the effective alignment is the maximum of the requested alignment, the
current alignment of `ptr` (if any) and `FIO_MEMORY_ALIGN_SIZE`;
- effective values above `FIO_MEMORY_SYS_ALLOCATION_SIZE` fail
(returns NULL, sets `errno` to `EINVAL`).
Data preservation semantics are identical to `realloc2` (`copy_len` bytes).
In-place growth may only occur when the existing pointer already satisfies
the requested alignment.
_Symbol type:_ `function`
#### `fio_malloc_aligned`
```c
void *FIO_MEM_ALIGN_NEW fio_malloc_aligned(size_t size, size_t alignment)
```
Allocates `size` bytes, returning a pointer aligned to (at least)
`alignment`. Same semantics as `realloc_aligned(NULL, size, 0, alignment)`.
_Symbol type:_ `function`
#### `fio_calloc_aligned`
```c
void *FIO_MEM_ALIGN_NEW fio_calloc_aligned(size_t size_per_unit, size_t unit_count, size_t alignment)
```
Same as `malloc_aligned(size_per_unit * unit_count, alignment)`,
except that the allocated memory is zeroed out.
_Symbol type:_ `function`
#### `fio_mmap`
```c
void *FIO_MEM_ALIGN_NEW fio_mmap(size_t size)
```
Allocates memory directly using `mmap`, this is preferred for objects that
both require almost a page of memory (or more) and expect a long lifetime.
However, since this allocation will invoke the system call (`mmap`), it will
be inherently slower.
`mempoll_free` can be used for deallocating the memory.
_Symbol type:_ `function`
#### `fio_malloc_after_fork`
```c
void fio_malloc_after_fork(void)
```
When forking is called manually, call this function to reset the facil.io
memory allocator's locks.
This is provided in case of forking in a multi-threaded environment, which
some people do even though it's bad.
_Symbol type:_ `function`
#### `fio_malloc_arenas`
```c
size_t fio_malloc_arenas(void)
```
Arena count for the allocator.
_Symbol type:_ `function`
#### `fio_malloc_block_size`
```c
size_t fio_malloc_block_size(void)
```
_Symbol type:_ `function`
#### `fio_malloc_print_state`
```c
void fio_malloc_print_state(void)
```
Prints the allocator's data structure. May be used for debugging.
_Symbol type:_ `function`
#### `fio_malloc_print_free_block_list`
```c
void fio_malloc_print_free_block_list(void)
```
Prints the allocator's free block list. May be used for debugging.
_Symbol type:_ `function`
#### `fio_malloc_print_settings`
```c
void fio_malloc_print_settings(void)
```
Prints the settings used to define the allocator.
_Symbol type:_ `function`
#### `fio_malloc_sys_alloc_size`
```c
inline size_t fio_malloc_sys_alloc_size(void)
```
System allocation sizes (bytes per system allocation).
_Symbol type:_ `function`
#### `fio_malloc_cache_slots`
```c
inline size_t fio_malloc_cache_slots(void)
```
Cached system allocations (free, but held on to).
_Symbol type:_ `function`
#### `fio_malloc_alignment`
```c
inline size_t fio_malloc_alignment(void)
```
Allocations alignment.
_Symbol type:_ `function`
#### `fio_malloc_alignment_log`
```c
inline size_t fio_malloc_alignment_log(void)
```
Allocations alignment log (base 2).
_Symbol type:_ `function`
#### `fio_malloc_alloc_limit`
```c
inline size_t fio_malloc_alloc_limit(void)
```
Allocation limit (at which point do we switch to system allocations?).
_Symbol type:_ `function`
#### `fio_malloc_arena_alloc_limit`
```c
inline size_t fio_malloc_arena_alloc_limit(void)
```
Allocation limit for arena based allocation (vs. big allocations).
_Symbol type:_ `function`
#### `fio_realloc_is_safe`
```c
inline size_t fio_realloc_is_safe(void)
```
_Symbol type:_ `function`
#### `fio_alloc_size`
```c
inline size_t fio_alloc_size(size_t minimum_bytes)
```
Returns the number of usable bytes the allocator will actually reserve for
an allocation request of `minimum_bytes` (the allocation's size class).
The result is alignment agnostic - alignment padding, if any, is reserved
in addition to the returned value.
NOTE: when the custom allocator is bypassed (`FIO_MEMORY_DISABLE`), the
system allocator exposes no rounding guarantee and `minimum_bytes` is
returned unchanged.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/011 string core.h`
103 public symbols.
### Macros
#### `FIO_STRING_WRITE_STR1`
```c
#define FIO_STRING_WRITE_STR1(str_) \
((fio_string_write_s){ \
.klass = 1, \
.info.str = {.len = (size_t)FIO_STRLEN((str_)), .buf = (str_)}})
```
A macro to add a String to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_STR2`
```c
#define FIO_STRING_WRITE_STR2(str_, len_) \
((fio_string_write_s){.klass = 1, .info.str = {.len = (len_), .buf = (str_)}})
```
A macro to add a String with known length to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_STR_INFO`
```c
#define FIO_STRING_WRITE_STR_INFO(str_) \
((fio_string_write_s){.klass = 1, \
.info.str = {.len = (str_).len, .buf = (str_).buf}})
```
A macro to add a String with known length to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_NUM`
```c
#define FIO_STRING_WRITE_NUM(num) \
((fio_string_write_s){.klass = 2, .info.i = (int64_t)(num)})
```
A macro to add a signed number to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_UNUM`
```c
#define FIO_STRING_WRITE_UNUM(num) \
((fio_string_write_s){.klass = 3, .info.u = (uint64_t)(num)})
```
A macro to add an unsigned number to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_HEX`
```c
#define FIO_STRING_WRITE_HEX(num) \
((fio_string_write_s){.klass = 4, .info.u = (uint64_t)(num)})
```
A macro to add a hex representation to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_BIN`
```c
#define FIO_STRING_WRITE_BIN(num) \
((fio_string_write_s){.klass = 5, .info.u = (uint64_t)(num)})
```
A macro to add a binary representation to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_FLOAT`
```c
#define FIO_STRING_WRITE_FLOAT(num) \
((fio_string_write_s){.klass = 6, .info.f = (double)(num)})
```
A macro to add a float (double) to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_SYS_REALLOC`
```c
#define FIO_STRING_SYS_REALLOC fio_string_sys_reallocate
```
Default reallocation callback implementation using libc `realloc`.
_Symbol type:_ `macro`
#### `FIO_STRING_REALLOC`
```c
#define FIO_STRING_REALLOC fio_string_default_reallocate
```
Default reallocation callback implementation using the default allocator
_Symbol type:_ `macro`
#### `FIO_STRING_ALLOC_COPY`
```c
#define FIO_STRING_ALLOC_COPY fio_string_default_allocate_copy
```
Default reallocation callback for memory that mustn't be freed.
_Symbol type:_ `macro`
#### `FIO_STRING_ALLOC_KEY`
```c
#define FIO_STRING_ALLOC_KEY fio_string_default_key_alloc
```
default allocator for the fio_keystr_s string data..
_Symbol type:_ `macro`
#### `FIO_STRING_FREE`
```c
#define FIO_STRING_FREE fio_string_default_free
```
Frees memory that was allocated with the default callbacks.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE2`
```c
#define FIO_STRING_FREE2 fio_string_default_free2
```
Frees memory that was allocated with the default callbacks.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_KEY`
```c
#define FIO_STRING_FREE_KEY fio_string_default_free_key
```
Frees memory that was allocated for a key string.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_NOOP`
```c
#define FIO_STRING_FREE_NOOP fio_string_default_free_noop
```
Does nothing.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_NOOP2`
```c
#define FIO_STRING_FREE_NOOP2 fio_string_default_free_noop2
```
Does nothing.
_Symbol type:_ `macro`
#### `FIO_KEYSTR_CONST`
```c
#define FIO_KEYSTR_CONST ((size_t)-1LL)
```
_Symbol type:_ `macro`
### Types
#### `fio_string_realloc_fn`
```c
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len)
```
A reallocation callback type for buffers in a `fio_str_info_s`.
The callback MUST allocate at least `len + 1` bytes, setting the new capacity
in `dest->capa`.
Returns 0 on success, -1 on error.
_Symbol type:_ `type`
#### `fio_string_write_s`
```c
typedef struct {
size_t klass;
union {
struct {
size_t len;
const char *buf;
} str;
double f;
int64_t i;
uint64_t u;
} info;
} fio_string_write_s
```
Argument type used by fio_string_write2.
_Symbol type:_ `type`
#### `fio_keystr_s`
```c
struct fio_keystr_s
```
a semi-opaque type used for the `fio_keystr` functions
_Symbol type:_ `type`
### Functions
#### `fio_string_write`
```c
FIO_SFUNC int fio_string_write(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *restrict src, size_t len)
```
Writes data to the end of the string in the `fio_string_s` struct,
returning an updated `fio_string_s` struct.
The returned string is NUL terminated if edited.
* `dest` an `fio_string_s` struct containing the destination string.
* `reallocate` is a callback that attempts to reallocate more memory (i.e.,
using `realloc`) and returns an updated `fio_string_s` struct containing the
updated capacity and buffer pointer (as well as the original length).
On failure the original `fio_string_s` should be returned. if
`reallocate` is NULL or fails, the data copied will be truncated.
* `src` is the data to be written to the end of `dest`.
* `len` is the length of the data to be written to the end of `dest`.
Note: this function performs only minimal checks and assumes that `dest` is
fully valid - i.e., that `dest.capa >= dest.len`, that `dest.buf` is
valid, etc'.
An example for a `reallocate` callback using the system's `realloc` function:
int fio_string_realloc_system(fio_str_info_s *dest, size_t len_no_nul) {
const size_t new_capa = fio_string_capa4len(len_no_nul);
void *tmp = realloc(dest.buf, new_capa);
if (!tmp)
return -1;
dest.capa = new_capa;
dest.buf = (char *)tmp;
return 0;
}
An example for using the function:
void example(void) {
char buf[32];
fio_str_info_s str = FIO_STR_INFO3(buf, 0, 32);
fio_string_write(&str, NULL, "The answer is: 0x", 17);
str.len += fio_ltoa(str.buf + str.len, 42, 16);
fio_string_write(&str, NULL, "!\n", 2);
printf("%s", str.buf);
}
_Symbol type:_ `function`
#### `fio_string_replace`
```c
int fio_string_replace(fio_str_info_s *dest, fio_string_realloc_fn reallocate, intptr_t start_pos, size_t overwrite_len, const void *src, size_t len)
```
Similar to `fio_string_write`, only replacing/inserting a sub-string in a
specific location.
Negative `start_pos` values are calculated backwards, `-1` == end of String.
When `overwrite_len` is zero, the function will insert the data at
`start_pos`, pushing existing data until after the inserted data.
If `overwrite_len` is non-zero, than `overwrite_len` bytes will be
overwritten (or deleted).
If `len == 0` than `src` will be ignored and the data marked for replacement
will be erased.
_Symbol type:_ `function`
#### `fio_string_write2`
```c
int fio_string_write2(fio_str_info_s *restrict dest, fio_string_realloc_fn reallocate, const fio_string_write_s srcs[])
```
Writes a group of objects (strings, numbers, etc') to `dest`.
`dest` and `reallocate` are similar to `fio_string_write`.
`src` is an array of `fio_string_write_s` structs, ending with a struct
that's all set to 0.
Use the `fio_string_write2` macro for ease, i.e.:
fio_str_info_s str = {0};
fio_string_write2(&str, my_reallocate,
FIO_STRING_WRITE_STR1("The answer is: "),
FIO_STRING_WRITE_NUM(42),
FIO_STRING_WRITE_STR2("(0x", 3),
FIO_STRING_WRITE_HEX(42),
FIO_STRING_WRITE_STR2(")", 1));
Note: this function might end up allocating more memory than absolutely
required as it favors fast performance over memory savings. It performs only
a single allocation (if any) and computes numeral string length only when
writing the numbers to the string.
_Symbol type:_ `function`
#### `fio_string_write2`
```c
#define fio_string_write2(dest, reallocate, ...) \
fio_string_write2((dest), \
(reallocate), \
(fio_string_write_s[]){__VA_ARGS__, {0}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_string_write_i`
```c
int fio_string_write_i(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_u`
```c
int fio_string_write_u(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_hex`
```c
int fio_string_write_hex(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_bin`
```c
int fio_string_write_bin(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_printf`
```c
FIO___PRINTF_STYLE(3, 0) int fio_string_printf( fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *format, ...)
```
Similar to fio_string_write, only using printf semantics.
_Symbol type:_ `function`
#### `fio_string_vprintf`
```c
FIO___PRINTF_STYLE(3, 0) int fio_string_vprintf( fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *format, va_list argv)
```
Similar to fio_string_write, only using vprintf semantics.
_Symbol type:_ `function`
#### `fio_string_write_escape`
```c
int fio_string_write_escape(fio_str_info_s *restrict dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data at the end of the String, escaping the data using JSON semantics.
The JSON semantic are common to many programming languages, promising a UTF-8
String while making it easy to read and copy the string during debugging.
_Symbol type:_ `function`
#### `fio_string_write_unescape`
```c
int fio_string_write_unescape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *enscaped, size_t enscaped_len)
```
Writes an escaped data into the string after un-escaping the data.
_Symbol type:_ `function`
#### `fio_string_write_base32enc`
```c
int fio_string_write_base32enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data to String using base64 encoding.
_Symbol type:_ `function`
#### `fio_string_write_base32dec`
```c
int fio_string_write_base32dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to String.
_Symbol type:_ `function`
#### `fio_string_write_base64enc`
```c
int fio_string_write_base64enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len, uint8_t url_encoded)
```
Writes data to String using base64 encoding.
_Symbol type:_ `function`
#### `fio_string_write_base64dec`
```c
int fio_string_write_base64dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to String.
_Symbol type:_ `function`
#### `fio_string_write_url_enc`
```c
int fio_string_write_url_enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data to String using URL encoding (a.k.a., percent encoding).
_Symbol type:_ `function`
#### `fio_string_write_url_dec`
```c
int fio_string_write_url_dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded URL data to String, decoding + to spaces.
_Symbol type:_ `function`
#### `fio_string_write_path_dec`
```c
int fio_string_write_path_dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded URL data to String, without decoding + to spaces.
_Symbol type:_ `function`
#### `fio_string_write_html_escape`
```c
int fio_string_write_html_escape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fio_string_write_html_unescape`
```c
int fio_string_write_html_unescape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *enscaped, size_t enscaped_len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fio_string_readfd`
```c
int fio_string_readfd(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int fd, intptr_t start_at, size_t limit)
```
Writes up to `limit` bytes from `fd` into `dest`, starting at `start_at`.
If `limit` is 0 (or less than 0) data will be written until EOF.
If `start_at` is negative, position will be calculated from the end of the
file where `-1 == EOF`.
Note: this will fail unless used on actual files (not sockets, not pipes).
_Symbol type:_ `function`
#### `fio_string_readfile`
```c
int fio_string_readfile(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *filename, intptr_t start_at, size_t limit)
```
Opens the file `filename` and pastes it's contents (or a slice ot it) at
the end of the String. If `limit == 0`, than the data will be read until
EOF.
If the file can't be located, opened or read, or if `start_at` is beyond
the EOF position, NULL is returned in the state's `data` field.
_Symbol type:_ `function`
#### `fio_string_getdelim_fd`
```c
int fio_string_getdelim_fd(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int fd, intptr_t start_at, char delim, size_t limit)
```
Writes up to `limit` bytes from `fd` into `dest`, starting at `start_at` and
ending either at the first occurrence of `delim` or at EOF.
If `limit` is 0 (or less than 0) as much data as may be required will be
written.
If `start_at` is negative, position will be calculated from the end of the
file where `-1 == EOF`.
Note: this will fail unless used on actual seekable files (not sockets, not
pipes).
_Symbol type:_ `function`
#### `fio_string_getdelim_file`
```c
int fio_string_getdelim_file(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *filename, intptr_t start_at, char delim, size_t limit)
```
Opens the file `filename`, calls `fio_string_getdelim_fd` and closes the
file.
_Symbol type:_ `function`
#### `fio_string_capa4len`
```c
inline size_t fio_string_capa4len(size_t new_len)
```
_Symbol type:_ `function`
#### `fio_string_default_reallocate`
```c
int fio_string_default_reallocate(fio_str_info_s *dst, size_t len)
```
default reallocation callback implementation.
_Symbol type:_ `function`
#### `fio_string_default_allocate_copy`
```c
int fio_string_default_allocate_copy(fio_str_info_s *dest, size_t new_capa)
```
default reallocation callback for memory that mustn't be freed.
_Symbol type:_ `function`
#### `fio_string_default_free`
```c
void fio_string_default_free(void *)
```
frees memory that was allocated with the default callbacks.
_Symbol type:_ `function`
#### `fio_string_default_free2`
```c
void fio_string_default_free2(fio_str_info_s str)
```
frees memory that was allocated with the default callbacks.
_Symbol type:_ `function`
#### `fio_string_default_free_noop`
```c
void fio_string_default_free_noop(void *)
```
does nothing.
_Symbol type:_ `function`
#### `fio_string_default_free_noop2`
```c
void fio_string_default_free_noop2(fio_str_info_s str)
```
does nothing.
_Symbol type:_ `function`
#### `fio_string_default_key_alloc`
```c
void *fio_string_default_key_alloc(size_t len)
```
default allocator for the fio_keystr_s string data..
_Symbol type:_ `function`
#### `fio_string_default_free_key`
```c
void fio_string_default_free_key(void *, size_t)
```
frees a fio_keystr_s memory that was allocated with the default callback.
_Symbol type:_ `function`
#### `fio_string_utf8_valid`
```c
bool fio_string_utf8_valid(fio_str_info_s str)
```
Returns 1 if the String is UTF-8 valid and 0 if not.
_Symbol type:_ `function`
#### `fio_string_utf8_len`
```c
size_t fio_string_utf8_len(fio_str_info_s str)
```
Returns the String's length in UTF-8 characters or 0 if invalid.
_Symbol type:_ `function`
#### `fio_string_utf8_valid_code_point`
```c
size_t fio_string_utf8_valid_code_point(const void *u8c, size_t buf_len)
```
Returns 0 if non-UTF-8 or returns 1-4 (UTF-8 if a valid char).
_Symbol type:_ `function`
#### `fio_string_utf8_select`
```c
int fio_string_utf8_select(fio_str_info_s str, intptr_t *pos, size_t *len)
```
Takes a UTF-8 character selection information (UTF-8 position and length)
and updates the same variables so they reference the raw byte slice
information.
If the String isn't UTF-8 valid up to the requested selection, than `pos`
will be updated to `-1` otherwise values are always positive.
The returned `len` value may be shorter than the original if there wasn't
enough data left to accommodate the requested length. When a `len` value of
`0` is returned, this means that `pos` marks the end of the String.
Returns -1 on error and 0 on success.
_Symbol type:_ `function`
#### `fio_string_is_greater_buf`
```c
int fio_string_is_greater_buf(fio_buf_info_s a, fio_buf_info_s b)
```
Compares two `fio_buf_info_s`, returning 1 if data in a is bigger than b.
Note: returns 0 if data in b is bigger than or equal(!).
_Symbol type:_ `function`
#### `fio_string_is_greater`
```c
inline int fio_string_is_greater(fio_str_info_s a, fio_str_info_s b)
```
Compares two strings, returning 1 if string a is bigger than string b.
Note: returns 0 if string b is bigger than string a or if strings are equal.
_Symbol type:_ `function`
#### `fio_bstr_reserve`
```c
inline char *fio_bstr_reserve(char *bstr, size_t len)
```
Reserves `len` for future `write` operations (used to minimize realloc).
_Symbol type:_ `function`
#### `fio_bstr_copy`
```c
inline char *fio_bstr_copy(char *bstr)
```
Copies a `fio_bstr` using "copy on write".
_Symbol type:_ `function`
#### `fio_bstr_free`
```c
inline void fio_bstr_free(char *bstr)
```
Frees a binary string allocated by a `fio_bstr` function. Returns NULL.
_Symbol type:_ `function`
#### `fio_bstr_info`
```c
inline fio_str_info_s fio_bstr_info(const char *bstr)
```
Returns information about the fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_buf`
```c
inline fio_buf_info_s fio_bstr_buf(const char *bstr)
```
Returns information about the fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_len`
```c
inline size_t fio_bstr_len(const char *bstr)
```
Gets the length of the fio_bstr. `bstr` MUST NOT be NULL.
_Symbol type:_ `function`
#### `fio_bstr_len_set`
```c
inline char *fio_bstr_len_set(char *bstr, size_t len)
```
Sets the length of the fio_bstr. `bstr` MUST NOT be NULL.
_Symbol type:_ `function`
#### `fio_bstr_is_greater`
```c
FIO_SFUNC int fio_bstr_is_greater(const char *a, const char *b)
```
Compares to see if fio_bstr a is greater than fio_bstr b (for FIO_SORT).
_Symbol type:_ `function`
#### `fio_bstr_is_eq`
```c
FIO_SFUNC int fio_bstr_is_eq(const char *a, const char *b)
```
Compares to see if fio_bstr a is equal to another fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_is_eq2info`
```c
FIO_SFUNC int fio_bstr_is_eq2info(const char *a_, fio_str_info_s b)
```
Compares to see if fio_bstr a is equal to another String.
_Symbol type:_ `function`
#### `fio_bstr_is_eq2buf`
```c
FIO_SFUNC int fio_bstr_is_eq2buf(const char *a_, fio_buf_info_s b)
```
Compares to see if fio_bstr a is equal to another String.
_Symbol type:_ `function`
#### `fio_bstr_write`
```c
inline char *fio_bstr_write(char *bstr, const void *restrict src, size_t len)
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
Returns existing string on reallocation error (true for all fio_bstr_write).
_Symbol type:_ `function`
#### `fio_bstr_replace`
```c
inline char *fio_bstr_replace(char *bstr, intptr_t start_pos, size_t overwrite_len, const void *src, size_t len)
```
Replaces data in a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write2`
```c
inline char *fio_bstr_write2(char *bstr, const fio_string_write_s srcs[])
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write2`
```c
#define fio_bstr_write2(bstr, ...) \
fio_bstr_write2(bstr, (fio_string_write_s[]){__VA_ARGS__, {0}})
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bstr_write_i`
```c
inline char *fio_bstr_write_i(char *bstr, int64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_u`
```c
inline char *fio_bstr_write_u(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_hex`
```c
inline char *fio_bstr_write_hex(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_bin`
```c
inline char *fio_bstr_write_bin(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_escape`
```c
inline char *fio_bstr_write_escape(char *bstr, const void *src, size_t len)
```
Writes escaped data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_unescape`
```c
inline char *fio_bstr_write_unescape(char *bstr, const void *src, size_t len)
```
Un-escapes and writes data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_base64enc`
```c
inline char *fio_bstr_write_base64enc(char *bstr, const void *src, size_t len, uint8_t url_encoded)
```
Writes base64 encoded data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_base64dec`
```c
inline char *fio_bstr_write_base64dec(char *bstr, const void *src, size_t len)
```
Decodes base64 data and writes to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_url_enc`
```c
inline char *fio_bstr_write_url_enc(char *bstr, const void *data, size_t len)
```
Writes data to String using URL encoding (a.k.a., percent encoding).
_Symbol type:_ `function`
#### `fio_bstr_write_url_dec`
```c
inline char *fio_bstr_write_url_dec(char *bstr, const void *encoded, size_t len)
```
Writes decoded URL data to String.
_Symbol type:_ `function`
#### `fio_bstr_write_html_escape`
```c
inline char *fio_bstr_write_html_escape(char *bstr, const void *raw, size_t len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fio_bstr_write_html_unescape`
```c
inline char *fio_bstr_write_html_unescape(char *bstr, const void *escaped, size_t len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fio_bstr_readfd`
```c
inline char *fio_bstr_readfd(char *bstr, int fd, intptr_t start_at, intptr_t limit)
```
Writes to the String from a regular file `fd`.
_Symbol type:_ `function`
#### `fio_bstr_readfile`
```c
inline char *fio_bstr_readfile(char *bstr, const char *filename, intptr_t start_at, intptr_t limit)
```
Writes to the String from a regular file named `filename`.
_Symbol type:_ `function`
#### `fio_bstr_getdelim_file`
```c
inline char *fio_bstr_getdelim_file(char *bstr, const char *filename, intptr_t start_at, char delim, size_t limit)
```
Writes to the String from a regular file named `filename`.
_Symbol type:_ `function`
#### `fio_bstr_getdelim_fd`
```c
inline char *fio_bstr_getdelim_fd(char *bstr, int fd, intptr_t start_at, char delim, size_t limit)
```
Writes to the String from a regular file `fd`.
_Symbol type:_ `function`
#### `fio_bstr_printf`
```c
inline FIO___PRINTF_STYLE(2, 0) char *fio_bstr_printf(char *bstr, const char *format, ...)
```
Writes a `fio_bstr` in `printf` style.
_Symbol type:_ `function`
#### `fio_bstr_reallocate`
```c
int fio_bstr_reallocate(fio_str_info_s *dest, size_t len)
```
default reallocation callback implementation - mostly for internal use.
_Symbol type:_ `function`
#### `fio_keystr_buf`
```c
inline fio_buf_info_s fio_keystr_buf(fio_keystr_s *str)
```
returns the Key String. NOTE: Key Strings are NOT NUL TERMINATED!
_Symbol type:_ `function`
#### `fio_keystr_info`
```c
inline fio_str_info_s fio_keystr_info(fio_keystr_s *str)
```
returns the Key String. NOTE: Key Strings are NOT NUL TERMINATED!
_Symbol type:_ `function`
#### `fio_keystr_tmp`
```c
inline fio_keystr_s fio_keystr_tmp(const char *buf, uint32_t len)
```
Returns a TEMPORARY `fio_keystr_s`.
_Symbol type:_ `function`
#### `fio_keystr_init`
```c
FIO_SFUNC fio_keystr_s fio_keystr_init(fio_str_info_s str, void *(*alloc_func)(size_t len))
```
Returns an initialized `fio_keystr_s` containing a copy of `str`.
_Symbol type:_ `function`
#### `fio_keystr_destroy`
```c
FIO_SFUNC void fio_keystr_destroy(fio_keystr_s *key, void (*free_func)(void *, size_t))
```
Destroys an initialized `fio_keystr_s`.
_Symbol type:_ `function`
#### `fio_keystr_is_eq`
```c
inline int fio_keystr_is_eq(fio_keystr_s a, fio_keystr_s b)
```
Compares two Key Strings.
_Symbol type:_ `function`
#### `fio_keystr_is_eq2`
```c
inline int fio_keystr_is_eq2(fio_keystr_s a_, fio_str_info_s b)
```
Compares a Key String to any String - used internally by the hash map.
_Symbol type:_ `function`
#### `fio_keystr_is_eq3`
```c
inline int fio_keystr_is_eq3(fio_keystr_s a_, fio_buf_info_s b)
```
Compares a Key String to any String - used internally by the hash map.
_Symbol type:_ `function`
#### `fio_keystr_hash`
```c
inline uint64_t fio_keystr_hash(fio_keystr_s a)
```
Returns a good-enough `fio_keystr_s` risky hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/012 gfm.h`
10 public symbols.
### Macros
#### `FIO_GFM_MAX_DEPTH`
```c
#define FIO_GFM_MAX_DEPTH 255
```
Maximum container nesting depth (blockquote + list).
_Symbol type:_ `macro`
#### `FIO_GFM_MAX_TABLE_COLUMNS`
```c
#define FIO_GFM_MAX_TABLE_COLUMNS 64
```
Maximum table columns (2 bits each → 4 columns per byte).
_Symbol type:_ `macro`
#### `FIO_GFM_REF_CACHE_SIZE`
```c
#define FIO_GFM_REF_CACHE_SIZE 128
```
Maximum reference definitions held in the fast cache.
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_GENERIC`
```c
#define FIO_GFM_ERR_GENERIC -1
```
Error codes — negative values are parser-generated.
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_DEPTH`
```c
#define FIO_GFM_ERR_DEPTH -2
```
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_INPUT`
```c
#define FIO_GFM_ERR_INPUT -3
```
_Symbol type:_ `macro`
#### `FIO_GFM_F_TIGHT`
```c
#define FIO_GFM_F_TIGHT ((uint8_t)1U << 0)
```
List is tight (no blank lines between items).
_Symbol type:_ `macro`
#### `FIO_GFM_F_LOOSE_SEEN`
```c
#define FIO_GFM_F_LOOSE_SEEN ((uint8_t)1U << 1)
```
A blank line was seen between items — list is loose.
_Symbol type:_ `macro`
#### `FIO_GFM_F_TASK`
```c
#define FIO_GFM_F_TASK ((uint8_t)1U << 2)
```
GFM task-list marker present.
_Symbol type:_ `macro`
#### `FIO_GFM_F_TASK_CHECKED`
```c
#define FIO_GFM_F_TASK_CHECKED ((uint8_t)1U << 3)
```
GFM task-list marker is checked.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/102 poll api.h`
16 public symbols.
### Macros
#### `FIO_POLL_POSSIBLE_FLAGS`
```c
#define FIO_POLL_POSSIBLE_FLAGS (POLLIN | POLLOUT | POLLPRI)
```
The user flags IO events recognize
_Symbol type:_ `macro`
#### `FIO_POLL_MAX_EVENTS`
```c
#define FIO_POLL_MAX_EVENTS 256
```
relevant only for epoll and kqueue - maximum number of events per review
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_EPOLL`
```c
#define FIO_POLL_ENGINE_EPOLL
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_KQUEUE`
```c
#define FIO_POLL_ENGINE_KQUEUE
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_POLL`
```c
#define FIO_POLL_ENGINE_POLL
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_STR`
```c
#define FIO_POLL_ENGINE_STR "epoll"
```
_Symbol type:_ `macro`
#### `FIO_POLL_VALIDATE`
```c
#define FIO_POLL_VALIDATE(settings_dest) \
if (!(settings_dest).on_data) \
(settings_dest).on_data = fio___poll_ev_mock; \
if (!(settings_dest).on_ready) \
(settings_dest).on_ready = fio___poll_ev_mock; \
if (!(settings_dest).on_close) \
(settings_dest).on_close = fio___poll_ev_mock;
```
_Symbol type:_ `macro`
### Types
#### `fio_poll_s`
```c
struct fio_poll_s
```
the `fio_poll_s` type should be considered opaque.
_Symbol type:_ `type`
#### `fio_poll_settings_s`
```c
typedef struct {
/** callback for when data is available in the incoming buffer. */
void (*on_data)(void *udata);
/** callback for when the outgoing buffer allows a call to `write`. */
void (*on_ready)(void *udata);
/** callback for closed connections and / or connections with errors. */
void (*on_close)(void *udata);
} fio_poll_settings_s
```
_Symbol type:_ `type`
### Functions
#### `fio_poll_init`
```c
inline void fio_poll_init(fio_poll_s *p, fio_poll_settings_s)
```
Initializes the polling object, allocating its resources.
_Symbol type:_ `function`
#### `fio_poll_init`
```c
#define fio_poll_init(p, ...) \
fio_poll_init((p), (fio_poll_settings_s){__VA_ARGS__})
```
Initializes the polling object, allocating its resources.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_poll_destroy`
```c
inline void fio_poll_destroy(fio_poll_s *p)
```
Destroys the polling object, freeing its resources.
_Symbol type:_ `function`
#### `fio_poll_engine`
```c
inline const char *fio_poll_engine(void)
```
returns the system call used for polling as a constant string.
_Symbol type:_ `function`
#### `fio_poll_monitor`
```c
int fio_poll_monitor(fio_poll_s *p, fio_socket_i fd, void *udata, unsigned short flags)
```
Adds a file descriptor to be monitored, adds events to be monitored or
updates the monitored file's `udata`.
Possible flags are: `POLLIN` and `POLLOUT`. Other flags may be set but might
be ignored.
Monitoring mode is always one-shot. If an event if fired, it is removed from
the monitoring state.
Returns -1 on error.
_Symbol type:_ `function`
#### `fio_poll_review`
```c
int fio_poll_review(fio_poll_s *p, size_t timeout)
```
Reviews if any of the monitored file descriptors has any events.
`timeout` is in milliseconds.
Returns the number of events called.
Polling is thread safe, but has different effects on different threads.
Adding a new file descriptor from one thread while polling in a different
thread will not poll that IO until `fio_poll_review` is called again.
_Symbol type:_ `function`
#### `fio_poll_forget`
```c
int fio_poll_forget(fio_poll_s *p, fio_socket_i fd)
```
Stops monitoring the specified file descriptor (if monitoring).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/102 queue.h`
28 public symbols.
### Macros
#### `FIO_QUEUE_TASKS_PER_ALLOC`
```c
#define FIO_QUEUE_TASKS_PER_ALLOC 338
```
_Symbol type:_ `macro`
#### `FIO_QUEUE_STATIC_INIT`
```c
#define FIO_QUEUE_STATIC_INIT(queue) \
{ \
.r = &(queue).mem, .w = &(queue).mem, \
.lock = (fio_thread_mutex_t)FIO_THREAD_MUTEX_INIT, \
.consumers = FIO_LIST_INIT((queue).consumers), \
}
```
May be used to initialize global, static memory, queues.
_Symbol type:_ `macro`
#### `FIO_TIMER_QUEUE_INIT`
```c
#define FIO_TIMER_QUEUE_INIT \
{ .lock = ((fio_thread_mutex_t)FIO_THREAD_MUTEX_INIT) }
```
_Symbol type:_ `macro`
### Types
#### `fio_queue_task_s`
```c
typedef struct {
/** The function to call */
void (*fn)(void *, void *);
/** User opaque data */
void *udata1;
/** User opaque data */
void *udata2;
} fio_queue_task_s
```
Task information
_Symbol type:_ `type`
#### `fio_queue_s`
```c
typedef struct {
/** task read pointer. */
fio___task_ring_s *r;
/** task write pointer. */
fio___task_ring_s *w;
/** the number of tasks waiting to be performed. */
uint32_t count;
/** global queue lock. */
FIO___LOCK_TYPE lock;
/** linked lists of consumer threads. */
FIO_LIST_NODE consumers;
/** main ring buffer associated with the queue. */
fio___task_ring_s mem;
} fio_queue_s
```
The queue object - should be considered opaque (or, at least, read only).
_Symbol type:_ `type`
#### `fio_timer_queue_s`
```c
typedef struct {
fio___timer_event_s *next;
FIO___LOCK_TYPE lock;
} fio_timer_queue_s
```
_Symbol type:_ `type`
#### `fio_timer_schedule_args_s`
```c
typedef struct {
/** The timer function. If it returns a non-zero value, the timer stops. */
int (*fn)(void *, void *);
/** Opaque user data. */
void *udata1;
/** Opaque user data. */
void *udata2;
/** Called when the timer is done (finished). */
void (*on_finish)(void *, void *);
/** Timer interval, in milliseconds. */
uint32_t every;
/** The number of times the timer should be performed. -1 == infinity. */
int32_t repetitions;
/** Millisecond at which to start. If missing, filled automatically. */
int64_t start_at;
} fio_timer_schedule_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_queue_init`
```c
inline void fio_queue_init(fio_queue_s *q)
```
Initializes a fio_queue_s object.
_Symbol type:_ `function`
#### `fio_queue_destroy`
```c
void fio_queue_destroy(fio_queue_s *q)
```
Destroys a queue and re-initializes it, after freeing any used resources.
_Symbol type:_ `function`
#### `fio_queue_new`
```c
fio_queue_s *fio_queue_new(void)
```
Creates a new queue object (allocated on the heap).
_Symbol type:_ `function`
#### `fio_queue_free`
```c
void fio_queue_free(fio_queue_s *q)
```
Frees a queue object after calling fio_queue_destroy.
_Symbol type:_ `function`
#### `fio_queue_push`
```c
int fio_queue_push(fio_queue_s *q, fio_queue_task_s task)
```
Pushes a task to the queue. Returns -1 on error.
_Symbol type:_ `function`
#### `fio_queue_push`
```c
#define fio_queue_push(q, ...) \
fio_queue_push((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the queue, offering named arguments for the task.
Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_queue_push_urgent`
```c
int fio_queue_push_urgent(fio_queue_s *q, fio_queue_task_s task)
```
Pushes a task to the head of the queue. Returns -1 on error (no memory).
_Symbol type:_ `function`
#### `fio_queue_push_urgent`
```c
#define fio_queue_push_urgent(q, ...) \
fio_queue_push_urgent((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the queue, offering named arguments for the task.
Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_queue_pop`
```c
fio_queue_task_s fio_queue_pop(fio_queue_s *q)
```
Pops a task from the queue (FIFO). Returns a NULL task on error.
_Symbol type:_ `function`
#### `fio_queue_perform`
```c
int fio_queue_perform(fio_queue_s *q)
```
Performs a task from the queue. Returns -1 on error (queue empty).
_Symbol type:_ `function`
#### `fio_queue_perform_all`
```c
void fio_queue_perform_all(fio_queue_s *q)
```
Performs all tasks in the queue.
_Symbol type:_ `function`
#### `fio_queue_count`
```c
inline uint32_t fio_queue_count(fio_queue_s *q)
```
returns the number of tasks in the queue.
_Symbol type:_ `function`
#### `fio_queue_workers_add`
```c
int fio_queue_workers_add(fio_queue_s *q, size_t count)
```
Adds worker / consumer threads to perform the jobs in the queue.
_Symbol type:_ `function`
#### `fio_queue_workers_stop`
```c
size_t fio_queue_workers_stop(fio_queue_s *q)
```
Signals all worker threads to stop performing tasks and terminate.
Returns the number of worker groups signaled. Does NOT wait and does NOT
promise that pending tasks are performed.
_Symbol type:_ `function`
#### `fio_queue_workers_join`
```c
void fio_queue_workers_join(fio_queue_s *q)
```
Signals all worker threads to stop, waiting for them to complete.
If the queue had workers, pending tasks are performed before returning.
_Symbol type:_ `function`
#### `fio_queue_workers_wake`
```c
void fio_queue_workers_wake(fio_queue_s *q)
```
Signals all worker threads to go back to work (new tasks added).
_Symbol type:_ `function`
#### `fio_timer_schedule`
```c
void fio_timer_schedule(fio_timer_queue_s *timer_queue, fio_timer_schedule_args_s args)
```
Adds a time-bound event to the timer queue.
_Symbol type:_ `function`
#### `fio_timer_schedule`
```c
#define fio_timer_schedule(timer_queue, ...) \
fio_timer_schedule((timer_queue), (fio_timer_schedule_args_s){__VA_ARGS__})
```
A MACRO allowing named arguments to be used. See fio_timer_schedule_args_s.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_timer_push2queue`
```c
size_t fio_timer_push2queue(fio_queue_s *queue, fio_timer_queue_s *timer_queue, int64_t now_in_milliseconds)
```
Pushes due events from the timer queue to an event queue.
_Symbol type:_ `function`
#### `fio_timer_next_at`
```c
inline int64_t fio_timer_next_at(fio_timer_queue_s *timer_queue)
```
_Symbol type:_ `function`
#### `fio_timer_destroy`
```c
void fio_timer_destroy(fio_timer_queue_s *timer_queue)
```
Clears any waiting timer bound tasks.
NOTE:
The timer queue must NEVER be freed when there's a chance that timer tasks
are waiting to be performed in a `fio_queue_s`.
This is due to the fact that the tasks may try to reschedule themselves (if
they repeat).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/102 stream.h`
16 public symbols.
### Macros
#### `FIO_STREAM_COPY_PER_PACKET`
```c
#define FIO_STREAM_COPY_PER_PACKET 98304
```
Break apart large memory blocks into smaller pieces. by default 96Kb
_Symbol type:_ `macro`
#### `FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`
```c
#define FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN 116
```
If the data added is less than said bytes, copy is preferred (locality).
_Symbol type:_ `macro`
#### `FIO_STREAM_INIT`
```c
#define FIO_STREAM_INIT(s) \
{ .next = NULL, .pos = &(s).next }
```
_Symbol type:_ `macro`
### Types
#### `fio_stream_packet_s`
```c
struct fio_stream_packet_s
```
_Symbol type:_ `type`
#### `fio_stream_s`
```c
typedef struct {
/* do not directly access! */
fio_stream_packet_s *next;
fio_stream_packet_s **pos;
size_t consumed;
size_t length;
} fio_stream_s
```
_Symbol type:_ `type`
### Functions
#### `fio_stream_new`
```c
inline fio_stream_s *fio_stream_new(void)
```
_Symbol type:_ `function`
#### `fio_stream_free`
```c
inline int fio_stream_free(fio_stream_s *stream)
```
_Symbol type:_ `function`
#### `fio_stream_destroy`
```c
void fio_stream_destroy(fio_stream_s *stream)
```
Destroys the object, re-initializing its container.
_Symbol type:_ `function`
#### `fio_stream_pack_data`
```c
fio_stream_packet_s *fio_stream_pack_data(void *buf, size_t len, size_t offset, uint8_t copy_buffer, void (*dealloc_func)(void *))
```
Packs data into a fio_stream_packet_s container.
_Symbol type:_ `function`
#### `fio_stream_pack_fd`
```c
fio_stream_packet_s *fio_stream_pack_fd(int fd, size_t len, size_t offset, uint8_t keep_open)
```
Packs a file descriptor into a fio_stream_packet_s container.
_Symbol type:_ `function`
#### `fio_stream_add`
```c
void fio_stream_add(fio_stream_s *stream, fio_stream_packet_s *packet)
```
Adds a packet to the stream. This isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_pack_free`
```c
void fio_stream_pack_free(fio_stream_packet_s *packet)
```
Destroys the fio_stream_packet_s - call this ONLY if unused.
_Symbol type:_ `function`
#### `fio_stream_read`
```c
void fio_stream_read(fio_stream_s *stream, char **buf, size_t *len)
```
Reads data from the stream (if any), leaving it in the stream.
`buf` MUST point to a buffer with - at least - `len` bytes. This is required
in case the packed data is fragmented or references a file and needs to be
copied to an available buffer.
On error, or if the stream is empty, `buf` will be set to NULL and `len` will
be set to zero.
Otherwise, `buf` may retain the same value or it may point directly to a
memory address within the stream's buffer (the original value may be lost)
and `len` will be updated to the largest possible value for valid data that
can be read from `buf`.
Note: this isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_advance`
```c
void fio_stream_advance(fio_stream_s *stream, size_t len)
```
Advances the Stream, so the first `len` bytes are marked as consumed.
Note: this isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_any`
```c
inline uint8_t fio_stream_any(fio_stream_s *stream)
```
Returns true if there's any data in the stream.
Note: this isn't truly thread safe.
_Symbol type:_ `function`
#### `fio_stream_length`
```c
inline size_t fio_stream_length(fio_stream_s *stream)
```
Returns the number of bytes waiting in the stream.
Note: this isn't truly thread safe.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/103 md2html.h`
2 public symbols.
### Macros
#### `FIO_MD2HTML`
```c
#define FIO_MD2HTML /* Development inclusion - ignore line */
```
_Symbol type:_ `macro`
#### `FIO_MD2HTML_ERR_ALLOC`
```c
#define FIO_MD2HTML_ERR_ALLOC 1
```
Markdown-to-HTML renderer callback error: output allocation failed.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/104 mustache.h`
14 public symbols.
### Macros
#### `FIO_MUSTACHE_MAX_DEPTH`
```c
#define FIO_MUSTACHE_MAX_DEPTH 128
```
The maximum depth of a template's context
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_PRESERVE_PADDING`
```c
#define FIO_MUSTACHE_PRESERVE_PADDING 0
```
Preserves padding for stand-alone variables and partial templates
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_LAMBDA_SUPPORT`
```c
#define FIO_MUSTACHE_LAMBDA_SUPPORT 0
```
Supports raw text for lambda style languages.
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_ISOLATE_PARTIALS`
```c
#define FIO_MUSTACHE_ISOLATE_PARTIALS 1
```
Limits the scope of partial templates to the context of their section.
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_SECURE_PATH`
```c
#define FIO_MUSTACHE_SECURE_PATH 1
```
_Symbol type:_ `macro`
### Types
#### `fio_mustache_s`
```c
struct fio_mustache_s
```
_Symbol type:_ `type`
#### `fio_mustache_bargs_s`
```c
struct fio_mustache_bargs_s
```
_Symbol type:_ `type`
#### `fio_mustache_load_args_s`
```c
typedef struct {
/** The file's content (if pre-loaded) */
fio_buf_info_s data;
/** The file's name (even if preloaded, used for partials load paths) */
fio_buf_info_s filename;
/** Loads the file's content, returning a `fio_buf_info_s` structure. */
fio_buf_info_s (*load_file_data)(fio_buf_info_s filename, void *udata);
/** Frees the file's content from its `fio_buf_info_s` structure. */
void (*free_file_data)(fio_buf_info_s file_data, void *udata);
/** Called when YAML front matter data was found. */
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
/** Opaque user data. */
void *udata;
} fio_mustache_load_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_mustache_load`
```c
fio_mustache_s *fio_mustache_load(fio_mustache_load_args_s settings)
```
_Symbol type:_ `function`
#### `fio_mustache_load`
```c
#define fio_mustache_load(...) \
fio_mustache_load((fio_mustache_load_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_mustache_free`
```c
void fio_mustache_free(fio_mustache_s *m)
```
_Symbol type:_ `function`
#### `fio_mustache_dup`
```c
fio_mustache_s *fio_mustache_dup(fio_mustache_s *m)
```
Increases the mustache template's reference count.
_Symbol type:_ `function`
#### `fio_mustache_build`
```c
void *fio_mustache_build(fio_mustache_s *m, fio_mustache_bargs_s)
```
Builds the template, returning the final value of `udata` (or NULL).
_Symbol type:_ `function`
#### `fio_mustache_build`
```c
#define fio_mustache_build(m, ...) \
fio_mustache_build((m), ((fio_mustache_bargs_s){__VA_ARGS__}))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/150 crypto core.h`
2 public symbols.
### Types
#### `fio_crypto_enc_fn`
```c
typedef void(fio_crypto_enc_fn)(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
_Symbol type:_ `type`
#### `fio_crypto_dec_fn`
```c
typedef int(fio_crypto_dec_fn)(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
_Symbol type:_ `type`
-----------------------------------------------------
# `./fio-stl/152 blake2.h`
14 public symbols.
### Types
#### `fio_blake2b_s`
```c
typedef struct {
uint64_t h[8]; /* state */
uint64_t t[2]; /* total bytes processed (128-bit counter) */
uint64_t f[2]; /* finalization flags */
uint8_t buf[128]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2b_s
```
Streaming BLAKE2b type (64-bit, up to 64-byte digest).
_Symbol type:_ `type`
#### `fio_blake2s_s`
```c
typedef struct {
uint32_t h[8]; /* state */
uint32_t t[2]; /* total bytes processed (64-bit counter) */
uint32_t f[2]; /* finalization flags */
uint8_t buf[64]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2s_s
```
Streaming BLAKE2s type (32-bit, up to 32-byte digest).
_Symbol type:_ `type`
### Functions
#### `fio_blake2b`
```c
inline fio_u512 fio_blake2b(const void *data, uint64_t len)
```
One-shot BLAKE2b hash with max-length (64 byte) digest.
_Symbol type:_ `function`
#### `fio_blake2b_hash`
```c
void fio_blake2b_hash(void *restrict out, size_t outlen, const void *restrict data, size_t len, const void *restrict key, size_t keylen)
```
Flexible-output BLAKE2b hash.
`out` must point to a buffer of at least `outlen` bytes.
`outlen` must be between 1 and 64 (default 64 if 0).
`key` and `keylen` are optional (set to NULL/0 for unkeyed hashing).
_Symbol type:_ `function`
#### `fio_blake2b_hmac`
```c
fio_u512 fio_blake2b_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
HMAC-BLAKE2b (64 byte key, 64 byte digest), compatible with SHA HMAC.
_Symbol type:_ `function`
#### `fio_blake2b_init`
```c
fio_blake2b_s fio_blake2b_init(size_t outlen, const void *key, size_t keylen)
```
Initialize a BLAKE2b streaming context. outlen: 1-64 (default 64).
_Symbol type:_ `function`
#### `fio_blake2b_consume`
```c
void fio_blake2b_consume(fio_blake2b_s *restrict h, const void *restrict data, size_t len)
```
Feed data into BLAKE2b hash.
_Symbol type:_ `function`
#### `fio_blake2b_finalize`
```c
fio_u512 fio_blake2b_finalize(fio_blake2b_s *h)
```
Finalize BLAKE2b hash. Returns result in fio_u512 (valid bytes = outlen).
_Symbol type:_ `function`
#### `fio_blake2s`
```c
inline fio_u256 fio_blake2s(const void *data, uint64_t len)
```
One-shot BLAKE2s hash with max-length (32 byte) digest.
_Symbol type:_ `function`
#### `fio_blake2s_hash`
```c
void fio_blake2s_hash(void *restrict out, size_t outlen, const void *restrict data, size_t len, const void *restrict key, size_t keylen)
```
Flexible-output BLAKE2s hash.
`out` must point to a buffer of at least `outlen` bytes.
`outlen` must be between 1 and 32 (default 32 if 0).
`key` and `keylen` are optional (set to NULL/0 for unkeyed hashing).
_Symbol type:_ `function`
#### `fio_blake2s_hmac`
```c
fio_u256 fio_blake2s_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
HMAC-BLAKE2s (32 byte key, 32 byte digest), compatible with SHA HMAC.
_Symbol type:_ `function`
#### `fio_blake2s_init`
```c
fio_blake2s_s fio_blake2s_init(size_t outlen, const void *key, size_t keylen)
```
Initialize a BLAKE2s streaming context. outlen: 1-32 (default 32).
_Symbol type:_ `function`
#### `fio_blake2s_consume`
```c
void fio_blake2s_consume(fio_blake2s_s *restrict h, const void *restrict data, size_t len)
```
Feed data into BLAKE2s hash.
_Symbol type:_ `function`
#### `fio_blake2s_finalize`
```c
fio_u256 fio_blake2s_finalize(fio_blake2s_s *h)
```
Finalize BLAKE2s hash. Returns result in fio_u256 (valid bytes = outlen).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 chacha20poly1305.h`
7 public symbols.
### Functions
#### `fio_chacha20_poly1305_enc`
```c
void fio_chacha20_poly1305_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place encryption of `data` using ChaCha20 with additional
data, producing a 16 byte message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
_Symbol type:_ `function`
#### `fio_chacha20_poly1305_dec`
```c
int fio_chacha20_poly1305_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place decryption of `data` using ChaCha20 after authenticating
the message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
* `mac` MUST point to a buffer where the 16 byte MAC is placed.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_xchacha20_poly1305_enc`
```c
void fio_xchacha20_poly1305_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place encryption of `data` using XChaCha20 with additional
data, producing a 16 byte message authentication code (MAC) using Poly1305.
XChaCha20 uses a 192-bit (24 byte) nonce, making it safe to use with
randomly-generated nonces without risk of nonce collision.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
_Symbol type:_ `function`
#### `fio_xchacha20_poly1305_dec`
```c
int fio_xchacha20_poly1305_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place decryption of `data` using XChaCha20 after
authenticating the message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
* `mac` MUST point to a buffer where the 16 byte MAC is placed.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_xchacha20`
```c
void fio_xchacha20(void *restrict data, size_t len, const void *key, const void *nonce, uint32_t counter)
```
Performs an in-place encryption/decryption of `data` using XChaCha20.
XChaCha20 uses a 192-bit (24 byte) nonce, making it safe to use with
randomly-generated nonces without risk of nonce collision.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `counter` is the block counter, usually 0 unless `data` is mid-cyphertext.
_Symbol type:_ `function`
#### `fio_chacha20`
```c
void fio_chacha20(void *restrict data, size_t len, const void *key, const void *nonce, uint32_t counter)
```
Performs an in-place encryption/decryption of `data` using ChaCha20.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `counter` is the block counter, usually 1 unless `data` is mid-cyphertext.
_Symbol type:_ `function`
#### `fio_poly1305_auth`
```c
void fio_poly1305_auth(void *restrict mac_dest, void *restrict message, size_t len, const void *ad, size_t ad_len, const void *key256bits)
```
Given a Poly1305 256bit (32 byte) key, writes the authentication code for the
poly message and additional data into `mac_dest`.
* `mac_dest` MUST point to a buffer with (at least) 16 available bytes.
* `message` MAY be omitted.
* `ad` MAY be omitted (additional data).
* `key` MUST point to a 256 bit long memory address (32 Bytes).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha1.h`
4 public symbols.
### Types
#### `fio_sha1_s`
```c
typedef union {
#ifdef __SIZEOF_INT128__
__uint128_t align__;
#else
uint64_t align__;
#endif
uint32_t v[5];
uint8_t digest[20];
} fio_sha1_s
```
The data type containing the SHA1 digest (result).
_Symbol type:_ `type`
### Functions
#### `fio_sha1`
```c
fio_sha1_s fio_sha1(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA1 hashing algorithm.
Do NOT use - SHA1 is broken... but for some reason some protocols still
require it's use (i.e., WebSockets), so it's here for your convenience.
_Symbol type:_ `function`
#### `fio_sha1_len`
```c
inline size_t fio_sha1_len(void)
```
Returns the digest length of SHA1 in bytes (20 bytes)
_Symbol type:_ `function`
#### `fio_sha1_digest`
```c
inline uint8_t *fio_sha1_digest(fio_sha1_s *s)
```
Returns the 20 Byte long digest of a SHA1 object.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha2.h`
14 public symbols.
### Types
#### `fio_sha256_s`
```c
typedef struct {
fio_u256 hash;
fio_u512 cache;
uint64_t total_len;
} fio_sha256_s
```
Streaming SHA-256 type.
_Symbol type:_ `type`
#### `fio_sha512_s`
```c
typedef struct {
fio_u512 hash;
fio_u1024 cache;
uint64_t total_len;
} fio_sha512_s
```
Streaming SHA-512 type.
_Symbol type:_ `type`
### Functions
#### `fio_sha256`
```c
inline fio_u256 fio_sha256(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-256 hashing algorithm.
_Symbol type:_ `function`
#### `fio_sha256_init`
```c
inline fio_sha256_s fio_sha256_init(void)
```
initializes a fio_u256 so the hash can consume streaming data.
_Symbol type:_ `function`
#### `fio_sha256_consume`
```c
void fio_sha256_consume(fio_sha256_s *h, const void *data, uint64_t len)
```
Feed data into the hash
_Symbol type:_ `function`
#### `fio_sha256_finalize`
```c
fio_u256 fio_sha256_finalize(fio_sha256_s *h)
```
finalizes a fio_u256 with the SHA 256 hash.
_Symbol type:_ `function`
#### `fio_sha512`
```c
inline fio_u512 fio_sha512(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-512 hashing algorithm.
_Symbol type:_ `function`
#### `fio_sha512_init`
```c
inline fio_sha512_s fio_sha512_init(void)
```
initializes a fio_u512 so the hash can consume streaming data.
_Symbol type:_ `function`
#### `fio_sha512_consume`
```c
void fio_sha512_consume(fio_sha512_s *h, const void *data, uint64_t len)
```
Feed data into the hash
_Symbol type:_ `function`
#### `fio_sha512_finalize`
```c
fio_u512 fio_sha512_finalize(fio_sha512_s *h)
```
finalizes a fio_u512 with the SHA 512 hash.
_Symbol type:_ `function`
#### `fio_sha384_init`
```c
inline fio_sha512_s fio_sha384_init(void)
```
initializes a fio_u512 so the hash can consume streaming data (SHA-384).
SHA-384 is SHA-512 with different initial values, truncated to 48 bytes.
NOTE: truncating a SHA-512 result is NOT the same as SHA-384.
_Symbol type:_ `function`
#### `fio_sha384_consume`
```c
#define fio_sha384_consume fio_sha512_consume
```
Feed data into the hash
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sha384_finalize`
```c
#define fio_sha384_finalize fio_sha512_finalize
```
finalizes a fio_u512 with the SHA 384 hash (first 48 bytes are the hash).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sha384`
```c
inline fio_u512 fio_sha384(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-384 hashing algorithm.
Returns a fio_u512 of which the first 48 bytes hold the SHA-384 hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha2z hkdf.h`
5 public symbols.
### Macros
#### `FIO_HKDF_SHA256_HASH_LEN`
```c
#define FIO_HKDF_SHA256_HASH_LEN 32
```
SHA-256 hash length (32 bytes).
_Symbol type:_ `macro`
#### `FIO_HKDF_SHA384_HASH_LEN`
```c
#define FIO_HKDF_SHA384_HASH_LEN 48
```
SHA-384 hash length (48 bytes).
_Symbol type:_ `macro`
### Functions
#### `fio_hkdf_extract`
```c
void fio_hkdf_extract(void *restrict prk, const void *restrict salt, size_t salt_len, const void *restrict ikm, size_t ikm_len, int use_sha384)
```
HKDF-Extract: PRK = HMAC-Hash(salt, IKM)
Extracts a pseudorandom key (PRK) from input keying material (IKM).
**Parameters:**
- `prk` - Output buffer (32 bytes for SHA-256, 48 for SHA-384)
- `salt` - Optional salt (if NULL, uses zeros of hash length)
- `salt_len` - Salt length in bytes
- `ikm` - Input keying material
- `ikm_len` - IKM length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_hkdf_expand`
```c
void fio_hkdf_expand(void *restrict okm, size_t okm_len, const void *restrict prk, size_t prk_len, const void *restrict info, size_t info_len, int use_sha384)
```
HKDF-Expand: OKM = HKDF-Expand(PRK, info, L)
Expands a pseudorandom key (PRK) into output keying material (OKM).
**Parameters:**
- `okm` - Output keying material buffer
- `okm_len` - Desired output length (max 255 * hash_len)
- `prk` - Pseudorandom key from Extract (32 or 48 bytes)
- `prk_len` - PRK length (32 for SHA-256, 48 for SHA-384)
- `info` - Optional context/application info
- `info_len` - Info length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_hkdf`
```c
void fio_hkdf(void *restrict okm, size_t okm_len, const void *restrict salt, size_t salt_len, const void *restrict ikm, size_t ikm_len, const void *restrict info, size_t info_len, int use_sha384)
```
Combined HKDF (Extract + Expand) - RFC 5869 Section 2.
Derives keying material from input keying material using HKDF.
**Parameters:**
- `okm` - Output keying material buffer
- `okm_len` - Desired output length (max 255 * hash_len)
- `salt` - Optional salt (if NULL, uses zeros of hash length)
- `salt_len` - Salt length in bytes
- `ikm` - Input keying material
- `ikm_len` - IKM length in bytes
- `info` - Optional context/application info
- `info_len` - Info length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha3.h`
17 public symbols.
### Types
#### `fio_sha3_s`
```c
typedef struct {
uint64_t state[25];
uint8_t buf[200]; /* rate buffer (max rate = 1600 - 2*capacity) */
size_t buflen; /* bytes in buffer */
size_t rate; /* rate in bytes */
size_t outlen; /* output length in bytes (0 for SHAKE) */
uint8_t delim; /* domain separation byte */
} fio_sha3_s
```
Keccak state (1600 bits = 200 bytes = 25 x 64-bit words)
_Symbol type:_ `type`
### Functions
#### `fio_sha3_224_init`
```c
inline fio_sha3_s fio_sha3_224_init(void)
```
Initialize SHA3-224 (28-byte output).
_Symbol type:_ `function`
#### `fio_sha3_256_init`
```c
inline fio_sha3_s fio_sha3_256_init(void)
```
Initialize SHA3-256 (32-byte output).
_Symbol type:_ `function`
#### `fio_sha3_384_init`
```c
inline fio_sha3_s fio_sha3_384_init(void)
```
Initialize SHA3-384 (48-byte output).
_Symbol type:_ `function`
#### `fio_sha3_512_init`
```c
inline fio_sha3_s fio_sha3_512_init(void)
```
Initialize SHA3-512 (64-byte output).
_Symbol type:_ `function`
#### `fio_sha3_consume`
```c
void fio_sha3_consume(fio_sha3_s *restrict h, const void *restrict data, size_t len)
```
Feed data into SHA3 hash.
_Symbol type:_ `function`
#### `fio_sha3_finalize`
```c
void fio_sha3_finalize(fio_sha3_s *restrict h, void *restrict out)
```
Finalize SHA3 hash. Writes h->outlen bytes to out.
_Symbol type:_ `function`
#### `fio_sha3_224`
```c
void fio_sha3_224(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-224 (28-byte output).
_Symbol type:_ `function`
#### `fio_sha3_256`
```c
void fio_sha3_256(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-256 (32-byte output).
_Symbol type:_ `function`
#### `fio_sha3_384`
```c
void fio_sha3_384(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-384 (48-byte output).
_Symbol type:_ `function`
#### `fio_sha3_512`
```c
void fio_sha3_512(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-512 (64-byte output).
_Symbol type:_ `function`
#### `fio_shake128_init`
```c
inline fio_sha3_s fio_shake128_init(void)
```
Initialize SHAKE128 (variable output length).
_Symbol type:_ `function`
#### `fio_shake256_init`
```c
inline fio_sha3_s fio_shake256_init(void)
```
Initialize SHAKE256 (variable output length).
_Symbol type:_ `function`
#### `fio_shake_consume`
```c
#define fio_shake_consume fio_sha3_consume
```
Feed data into SHAKE.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_shake_squeeze`
```c
void fio_shake_squeeze(fio_sha3_s *restrict h, void *restrict out, size_t outlen)
```
Squeeze output from SHAKE. Can be called multiple times.
_Symbol type:_ `function`
#### `fio_shake128`
```c
void fio_shake128(void *restrict out, size_t outlen, const void *restrict data, size_t len)
```
Simple SHAKE128 with specified output length.
_Symbol type:_ `function`
#### `fio_shake256`
```c
void fio_shake256(void *restrict out, size_t outlen, const void *restrict data, size_t len)
```
Simple SHAKE256 with specified output length.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/153 aes.h`
4 public symbols.
### Functions
#### `fio_aes128_gcm_enc`
```c
void fio_aes128_gcm_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-128-GCM encryption with authentication.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
* `key` MUST point to a 128 bit key (16 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
_Symbol type:_ `function`
#### `fio_aes128_gcm_dec`
```c
int fio_aes128_gcm_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-128-GCM decryption with authentication verification.
* `mac` MUST point to a buffer with the 16 byte MAC to verify.
* `key` MUST point to a 128 bit key (16 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_aes256_gcm_enc`
```c
void fio_aes256_gcm_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-256-GCM encryption with authentication.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
* `key` MUST point to a 256 bit key (32 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
_Symbol type:_ `function`
#### `fio_aes256_gcm_dec`
```c
int fio_aes256_gcm_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-256-GCM decryption with authentication verification.
* `mac` MUST point to a buffer with the 16 byte MAC to verify.
* `key` MUST point to a 256 bit key (32 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 ed25519.h`
13 public symbols.
### Macros
#### `FIO_X25519_CIPHERTEXT_LEN`
```c
#define FIO_X25519_CIPHERTEXT_LEN(message_len) ((message_len) + 48)
```
Returns the ciphertext length for a given plaintext length.
Ciphertext = ephemeral_pk (32) + mac (16) + encrypted_message (message_len)
_Symbol type:_ `macro`
#### `FIO_X25519_PLAINTEXT_LEN`
```c
#define FIO_X25519_PLAINTEXT_LEN(ciphertext_len) \
((ciphertext_len) > 48 ? ((ciphertext_len)-48) : 0)
```
Returns the plaintext length for a given ciphertext length.
Returns 0 if ciphertext_len < 48 (invalid ciphertext).
_Symbol type:_ `macro`
### Functions
#### `fio_ed25519_keypair`
```c
void fio_ed25519_keypair(uint8_t secret_key[32], uint8_t public_key[32])
```
Generates a new random Ed25519 key pair.
The secret key must be kept secret and securely erased when no longer
needed. The public key can be freely shared.
_Symbol type:_ `function`
#### `fio_ed25519_public_key`
```c
void fio_ed25519_public_key(uint8_t public_key[32], const uint8_t secret_key[32])
```
Derives the public key from an Ed25519 secret key.
Useful when the secret key is loaded from storage and the public key
needs to be recomputed.
_Symbol type:_ `function`
#### `fio_ed25519_sign`
```c
int fio_ed25519_sign(uint8_t signature[64], const void *message, size_t len, const uint8_t secret_key[32], const uint8_t public_key[32])
```
Signs a message using Ed25519.
The signature is 64 bytes and is deterministic (same message + key = same
signature).
_Symbol type:_ `function`
#### `fio_ed25519_verify`
```c
int fio_ed25519_verify(const uint8_t signature[64], const void *message, size_t len, const uint8_t public_key[32])
```
Verifies an Ed25519 signature.
Returns 0 on success (valid signature), -1 on failure (invalid signature).
_Symbol type:_ `function`
#### `fio_x25519_keypair`
```c
void fio_x25519_keypair(uint8_t secret_key[32], uint8_t public_key[32])
```
Generates a new random X25519 key pair.
The secret key must be kept secret. The public key can be shared with
the other party for key exchange.
_Symbol type:_ `function`
#### `fio_x25519_public_key`
```c
void fio_x25519_public_key(uint8_t public_key[32], const uint8_t secret_key[32])
```
Derives the public key from an X25519 secret key.
This performs scalar multiplication of the secret key with the base point.
_Symbol type:_ `function`
#### `fio_x25519_shared_secret`
```c
int fio_x25519_shared_secret(uint8_t shared_secret[32], const uint8_t secret_key[32], const uint8_t their_public_key[32])
```
Computes a shared secret using X25519 (ECDH).
Both parties compute the same shared secret:
shared = X25519(my_secret, their_public)
The shared secret should be passed through a KDF (e.g., HKDF with SHA-256)
before being used as an encryption key.
Returns 0 on success, -1 on failure (e.g., if their_public is a low-order
point, which would result in an all-zero shared secret).
_Symbol type:_ `function`
#### `fio_ed25519_sk_to_x25519`
```c
void fio_ed25519_sk_to_x25519(uint8_t x_secret_key[32], const uint8_t ed_secret_key[32])
```
Converts an Ed25519 secret key to an X25519 secret key.
This allows using an Ed25519 signing key for X25519 key exchange.
_Symbol type:_ `function`
#### `fio_ed25519_pk_to_x25519`
```c
void fio_ed25519_pk_to_x25519(uint8_t x_public_key[32], const uint8_t ed_public_key[32])
```
Converts an Ed25519 public key to an X25519 public key.
This allows encrypting to someone who has only shared their Ed25519
signing public key.
_Symbol type:_ `function`
#### `fio_x25519_encrypt`
```c
int fio_x25519_encrypt(uint8_t *ciphertext, const void *message, size_t message_len, fio_crypto_enc_fn encryption_function, const uint8_t recipient_pk[32])
```
Encrypts a message using the recipient's X25519 public key.
The ciphertext includes:
- 32 bytes: ephemeral public key (for key agreement)
- 16 bytes: authentication tag (MAC)
- N bytes: encrypted message
Total ciphertext size = message_len + 48 bytes
**Parameters:**
- `ciphertext` - Output buffer (must be at least message_len + 48 bytes)
- `message` - The plaintext message to encrypt
- `message_len` - Length of the message
- `encryption_function` - Encryption function (fio_chacha20_poly1305_enc)
- `recipient_pk` - The recipient's X25519 public key (32 bytes)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_x25519_decrypt`
```c
int fio_x25519_decrypt(uint8_t *plaintext, const uint8_t *ciphertext, size_t ciphertext_len, fio_crypto_dec_fn decryption_function, const uint8_t recipient_sk[32])
```
Decrypts a message using the recipient's X25519 secret key.
**Parameters:**
- `plaintext` - Output buffer (must be at least ciphertext_len - 48 bytes)
- `ciphertext` - The ciphertext (ephemeral_pk || mac || encrypted_data)
- `ciphertext_len` - Length of the ciphertext (must be >= 48)
- `decryption_function` - Decryption function (fio_chacha20_poly1305_dec)
- `recipient_sk` - The recipient's X25519 secret key (32 bytes)
**Returns:**
- 0 on success, -1 on failure (authentication failed or invalid input)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 p256.h`
5 public symbols.
### Functions
#### `fio_ecdsa_p256_verify`
```c
int fio_ecdsa_p256_verify(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, const uint8_t *pubkey, size_t pubkey_len)
```
Verifies an ECDSA P-256 signature.
**Parameters:**
- `sig` - DER-encoded signature (SEQUENCE { r INTEGER, s INTEGER })
- `sig_len` - Length of signature in bytes
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `pubkey` - Uncompressed public key (65 bytes: 0x04 || x || y)
- `pubkey_len` - Length of public key (must be 65)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_ecdsa_p256_sign`
```c
int fio_ecdsa_p256_sign(uint8_t *sig, size_t *sig_len, size_t sig_capacity, const uint8_t msg_hash[32], const uint8_t secret_key[32])
```
Signs a message hash using ECDSA P-256.
**Parameters:**
- `sig` - Output: DER-encoded signature (max 72 bytes)
- `sig_len` - Output: actual signature length
- `sig_capacity` - Capacity of signature buffer (should be >= 72)
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `secret_key` - 32-byte secret key (scalar, big-endian)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_ecdsa_p256_verify_raw`
```c
int fio_ecdsa_p256_verify_raw(const uint8_t r[32], const uint8_t s[32], const uint8_t msg_hash[32], const uint8_t pubkey_x[32], const uint8_t pubkey_y[32])
```
Verifies an ECDSA P-256 signature with raw r,s values.
**Parameters:**
- `r` - The r component of the signature (32 bytes, big-endian)
- `s` - The s component of the signature (32 bytes, big-endian)
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `pubkey_x` - X coordinate of public key (32 bytes, big-endian)
- `pubkey_y` - Y coordinate of public key (32 bytes, big-endian)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_p256_keypair`
```c
int fio_p256_keypair(uint8_t secret_key[32], uint8_t public_key[65])
```
Generates a P-256 keypair for ECDHE key exchange.
**Parameters:**
- `secret_key` - Output: 32-byte secret key (scalar, big-endian)
- `public_key` - Output: 65-byte uncompressed public key (0x04 || x || y)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_p256_shared_secret`
```c
int fio_p256_shared_secret(uint8_t shared_secret[32], const uint8_t secret_key[32], const uint8_t *their_public_key, size_t their_public_key_len)
```
Computes P-256 ECDH shared secret.
**Parameters:**
- `shared_secret` - Output: 32-byte shared secret (x-coordinate of result)
- `secret_key` - 32-byte secret key (scalar, big-endian)
- `their_public_key` - Their public key (uncompressed 65 bytes, or compressed 33 bytes)
- `their_public_key_len` - Length of their public key (33 or 65)
**Returns:**
- 0 on success, -1 on failure (invalid key, point at infinity, etc.)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 p384.h`
2 public symbols.
### Functions
#### `fio_ecdsa_p384_verify`
```c
int fio_ecdsa_p384_verify(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, const uint8_t *pubkey, size_t pubkey_len)
```
Verifies an ECDSA P-384 signature.
**Parameters:**
- `sig` - DER-encoded signature (SEQUENCE { r INTEGER, s INTEGER })
- `sig_len` - Length of signature in bytes
- `msg_hash` - SHA-384 hash of the message (48 bytes)
- `pubkey` - Uncompressed public key (97 bytes: 0x04 || x || y)
- `pubkey_len` - Length of public key (must be 97)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_ecdsa_p384_verify_raw`
```c
int fio_ecdsa_p384_verify_raw(const uint8_t r[48], const uint8_t s[48], const uint8_t msg_hash[48], const uint8_t pubkey_x[48], const uint8_t pubkey_y[48])
```
Verifies an ECDSA P-384 signature with raw r,s values.
**Parameters:**
- `r` - The r component of the signature (48 bytes, big-endian)
- `s` - The s component of the signature (48 bytes, big-endian)
- `msg_hash` - SHA-384 hash of the message (48 bytes)
- `pubkey_x` - X coordinate of public key (48 bytes, big-endian)
- `pubkey_y` - Y coordinate of public key (48 bytes, big-endian)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/155 der.h`
18 public symbols.
### Types
#### `fio_der_tag_e`
```c
typedef enum {
FIO_DER_EOC = 0x00, /**< End-of-contents */
FIO_DER_BOOLEAN = 0x01, /**< Boolean */
FIO_DER_INTEGER = 0x02, /**< Integer */
FIO_DER_BIT_STRING = 0x03, /**< Bit String */
FIO_DER_OCTET_STRING = 0x04, /**< Octet String */
FIO_DER_NULL = 0x05, /**< Null */
FIO_DER_OID = 0x06, /**< Object Identifier */
FIO_DER_OBJECT_DESCRIPTOR = 0x07, /**< Object Descriptor */
FIO_DER_EXTERNAL = 0x08, /**< External */
FIO_DER_REAL = 0x09, /**< Real (float) */
FIO_DER_ENUMERATED = 0x0A, /**< Enumerated */
FIO_DER_EMBEDDED_PDV = 0x0B, /**< Embedded PDV */
FIO_DER_UTF8_STRING = 0x0C, /**< UTF-8 String */
FIO_DER_RELATIVE_OID = 0x0D, /**< Relative OID */
FIO_DER_SEQUENCE = 0x10, /**< Sequence (0x30 with constructed bit) */
FIO_DER_SET = 0x11, /**< Set (0x31 with constructed bit) */
FIO_DER_NUMERIC_STRING = 0x12, /**< Numeric String */
FIO_DER_PRINTABLE_STRING = 0x13, /**< Printable String */
FIO_DER_T61_STRING = 0x14, /**< T61 String (Teletex) */
FIO_DER_VIDEOTEX_STRING = 0x15, /**< Videotex String */
FIO_DER_IA5_STRING = 0x16, /**< IA5 String (ASCII) */
FIO_DER_UTC_TIME = 0x17, /**< UTC Time */
FIO_DER_GENERALIZED_TIME = 0x18, /**< Generalized Time */
FIO_DER_GRAPHIC_STRING = 0x19, /**< Graphic String */
FIO_DER_VISIBLE_STRING = 0x1A, /**< Visible String */
FIO_DER_GENERAL_STRING = 0x1B, /**< General String */
FIO_DER_UNIVERSAL_STRING = 0x1C, /**< Universal String */
FIO_DER_BMP_STRING = 0x1E, /**< BMP String (UCS-2) */
/* Context-specific tags (0x80 | tag_number) with constructed bit (0x20) */
FIO_DER_CONTEXT_0 = 0xA0, /**< [0] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_1 = 0xA1, /**< [1] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_2 = 0xA2, /**< [2] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_3 = 0xA3, /**< [3] EXPLICIT/IMPLICIT */
} fio_der_tag_e
```
ASN.1 Universal Tag Types
_Symbol type:_ `type`
#### `fio_der_class_e`
```c
typedef enum {
FIO_DER_CLASS_UNIVERSAL = 0, /**< Universal (built-in types) */
FIO_DER_CLASS_APPLICATION = 1, /**< Application-specific */
FIO_DER_CLASS_CONTEXT = 2, /**< Context-specific */
FIO_DER_CLASS_PRIVATE = 3, /**< Private */
} fio_der_class_e
```
ASN.1 Tag Class (bits 7-6 of tag byte)
_Symbol type:_ `type`
#### `fio_der_element_s`
```c
typedef struct {
const uint8_t *data; /**< Pointer to element content (after tag+length) */
size_t len; /**< Length of content */
uint8_t tag; /**< Raw tag byte */
uint8_t is_constructed; /**< 1 if constructed (contains other elements) */
uint8_t tag_class; /**< 0=Universal, 1=Application, 2=Context, 3=Private */
uint8_t tag_number; /**< Tag number (bits 4-0, or extended) */
} fio_der_element_s
```
Parsed ASN.1 DER element
_Symbol type:_ `type`
#### `fio_der_iterator_s`
```c
typedef struct {
const uint8_t *pos; /**< Current position */
const uint8_t *end; /**< End of sequence */
} fio_der_iterator_s
```
Iterator for SEQUENCE or SET contents
_Symbol type:_ `type`
### Functions
#### `fio_der_parse`
```c
const uint8_t *fio_der_parse(fio_der_element_s *elem, const uint8_t *data, size_t data_len)
```
Parse one ASN.1 element from DER-encoded data.
**Parameters:**
- `elem` - Output structure to fill with parsed element info
- `data` - Pointer to DER-encoded data
- `data_len` - Length of data buffer
**Returns:**
- Pointer to next element (after this one), or NULL on error
_Symbol type:_ `function`
#### `fio_der_element_total_len`
```c
inline size_t fio_der_element_total_len(const fio_der_element_s *elem, const uint8_t *data)
```
Get the total encoded length of an ASN.1 element (tag + length + content).
**Parameters:**
- `elem` - Parsed element
- `data` - Original data pointer where element was parsed from
**Returns:**
- Total bytes used by the element encoding
_Symbol type:_ `function`
#### `fio_der_parse_integer`
```c
int fio_der_parse_integer(const fio_der_element_s *elem, uint64_t *value)
```
Parse an ASN.1 INTEGER element.
For small integers (<= 64-bit), sets *value.
For large integers (RSA modulus), use elem->data/len directly.
Leading zero bytes for positive numbers are handled correctly.
**Parameters:**
- `elem` - Parsed element (must be INTEGER type)
- `value` - Output for integer value (can be NULL for large integers)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_bit_string`
```c
int fio_der_parse_bit_string(const fio_der_element_s *elem, const uint8_t **bits, size_t *bit_len, uint8_t *unused_bits)
```
Parse an ASN.1 BIT STRING element.
**Parameters:**
- `elem` - Parsed element (must be BIT STRING type)
- `bits` - Output pointer to bit data (first byte is unused bits count)
- `bit_len` - Output length of bit data in bytes
- `unused_bits` - Output number of unused bits in last byte (0-7)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_oid`
```c
int fio_der_parse_oid(const fio_der_element_s *elem, char *buf, size_t buf_len)
```
Parse an ASN.1 OID into a dot-separated string.
Example output: "1.2.840.113549.1.1.11"
**Parameters:**
- `elem` - Parsed element (must be OID type)
- `buf` - Output buffer for string
- `buf_len` - Buffer size
**Returns:**
- Number of chars written (excluding NUL), or -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_time`
```c
int fio_der_parse_time(const fio_der_element_s *elem, int64_t *unix_time)
```
Parse an ASN.1 time (UTC Time or Generalized Time) to Unix timestamp.
**Parameters:**
- `elem` - Parsed element (must be UTC_TIME or GENERALIZED_TIME type)
- `unix_time` - Output Unix timestamp (seconds since 1970-01-01 00:00:00
UTC)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_string`
```c
inline const char *fio_der_parse_string(const fio_der_element_s *elem, size_t *len)
```
Parse an ASN.1 string element.
Supports UTF8String, PrintableString, IA5String, etc.
Returns pointer directly into the element data (no copy).
**Parameters:**
- `elem` - Parsed element (must be a string type)
- `len` - Output length of string
**Returns:**
- Pointer to string data, or NULL on error
_Symbol type:_ `function`
#### `fio_der_parse_boolean`
```c
inline int fio_der_parse_boolean(const fio_der_element_s *elem, int *value)
```
Parse an ASN.1 BOOLEAN element.
**Parameters:**
- `elem` - Parsed element (must be BOOLEAN type)
- `value` - Output boolean value (0 = false, non-zero = true)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_iterator_init`
```c
inline void fio_der_iterator_init(fio_der_iterator_s *it, const fio_der_element_s *sequence)
```
Initialize an iterator for a SEQUENCE or SET element.
**Parameters:**
- `it` - Iterator to initialize
- `sequence` - Parsed element (must be SEQUENCE or SET)
_Symbol type:_ `function`
#### `fio_der_iterator_next`
```c
int fio_der_iterator_next(fio_der_iterator_s *it, fio_der_element_s *elem)
```
Get the next element from an iterator.
**Parameters:**
- `it` - Iterator (updated to point to next element)
- `elem` - Output for parsed element
**Returns:**
- 0 if element available, -1 if end or error
_Symbol type:_ `function`
#### `fio_der_iterator_has_next`
```c
inline int fio_der_iterator_has_next(const fio_der_iterator_s *it)
```
Check if iterator has more elements.
**Parameters:**
- `it` - Iterator
**Returns:**
- 1 if more elements available, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_is_tag`
```c
inline int fio_der_is_tag(const fio_der_element_s *elem, uint8_t tag)
```
Check if an element is a specific tag type.
**Parameters:**
- `elem` - Parsed element
- `tag` - Expected tag (e.g., FIO_DER_INTEGER)
**Returns:**
- 1 if match, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_is_context_tag`
```c
inline int fio_der_is_context_tag(const fio_der_element_s *elem, uint8_t tag_num)
```
Check if an element is a context-specific tag.
**Parameters:**
- `elem` - Parsed element
- `tag_num` - Context tag number (0-31)
**Returns:**
- 1 if match, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_tag_number`
```c
inline uint8_t fio_der_tag_number(const fio_der_element_s *elem)
```
Get the tag number from an element.
For universal tags, returns the tag value (0-30).
For context-specific tags, returns the context number.
**Parameters:**
- `elem` - Parsed element
**Returns:**
- Tag number
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/155 rsa.h`
9 public symbols.
### Macros
#### `FIO_RSA_MAX_BITS`
```c
#define FIO_RSA_MAX_BITS 4096
```
Maximum RSA key size in bits
_Symbol type:_ `macro`
#### `FIO_RSA_MAX_BYTES`
```c
#define FIO_RSA_MAX_BYTES (FIO_RSA_MAX_BITS / 8)
```
Maximum RSA key size in bytes
_Symbol type:_ `macro`
#### `FIO_RSA_MAX_WORDS`
```c
#define FIO_RSA_MAX_WORDS (FIO_RSA_MAX_BYTES / 8)
```
Maximum RSA key size in 64-bit words
_Symbol type:_ `macro`
### Types
#### `fio_rsa_hash_e`
```c
typedef enum {
FIO_RSA_HASH_SHA256 = 0, /**< SHA-256 (32 bytes) */
FIO_RSA_HASH_SHA384 = 1, /**< SHA-384 (48 bytes) */
FIO_RSA_HASH_SHA512 = 2, /**< SHA-512 (64 bytes) */
} fio_rsa_hash_e
```
Hash algorithm identifiers for RSA verification
_Symbol type:_ `type`
#### `fio_rsa_pubkey_s`
```c
typedef struct {
const uint8_t *n; /**< Modulus (big-endian) */
size_t n_len; /**< Modulus length in bytes */
const uint8_t *e; /**< Public exponent (big-endian) */
size_t e_len; /**< Exponent length in bytes */
} fio_rsa_pubkey_s
```
RSA public key for signature verification.
The modulus (n) and exponent (e) are stored as big-endian byte arrays.
This matches the DER encoding used in X.509 certificates.
_Symbol type:_ `type`
#### `fio_rsa_privkey_s`
```c
typedef struct {
const uint8_t *n; /**< Modulus (big-endian) */
size_t n_len; /**< Modulus length in bytes (256, 384, or 512) */
const uint8_t *d; /**< Private exponent (big-endian) */
size_t d_len; /**< Private exponent length in bytes */
const uint8_t *e; /**< Public exponent (big-endian), optional, for blinding */
size_t e_len;
const uint8_t *p; /**< Prime p (big-endian), optional, for CRT */
size_t p_len;
const uint8_t *q; /**< Prime q (big-endian), optional, for CRT */
size_t q_len;
const uint8_t *dP; /**< d mod (p-1) (big-endian), optional, for CRT */
size_t dP_len;
const uint8_t *dQ; /**< d mod (q-1) (big-endian), optional, for CRT */
size_t dQ_len;
const uint8_t *qInv; /**< q^-1 mod p (big-endian), optional, for CRT */
size_t qInv_len;
} fio_rsa_privkey_s
```
RSA private key for signature generation.
The modulus (n) and private exponent (d) are stored as big-endian byte
arrays. This matches the DER encoding used in PKCS#8 private keys.
Optional CRT parameters (p, q, dP, dQ, qInv) and the public exponent (e)
may be provided. When CRT parameters are available, signing uses CRT with
message blinding for better side-channel resistance. When only n and d are
available, signing falls back to a non-CRT (still constant-time) path.
All optional fields are indicated by a non-zero length. Missing CRT
parameters may be derived from p, q, and d when p and q are present.
_Symbol type:_ `type`
### Functions
#### `fio_rsa_verify_pkcs1`
```c
int fio_rsa_verify_pkcs1(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_pubkey_s *key)
```
Verify an RSA PKCS#1 v1.5 signature.
This verifies signatures with DigestInfo encoding as used in:
- sha256WithRSAEncryption (OID 1.2.840.113549.1.1.11)
- sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12)
- sha512WithRSAEncryption (OID 1.2.840.113549.1.1.13)
**Parameters:**
- `sig` - Signature bytes (same length as modulus)
- `sig_len` - Signature length in bytes
- `msg_hash` - Pre-computed hash of the message
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm used (FIO_RSA_HASH_SHA256, etc.)
- `key` - RSA public key
**Returns:**
- 0 on success (valid signature), -1 on failure
_Symbol type:_ `function`
#### `fio_rsa_verify_pss`
```c
int fio_rsa_verify_pss(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_pubkey_s *key)
```
Verify an RSA-PSS signature (required for TLS 1.3).
RSA-PSS uses probabilistic padding and is the mandatory signature scheme
for TLS 1.3 CertificateVerify messages with RSA keys.
This implementation uses:
- MGF1 with the same hash function
- Salt length = hash length (as required by TLS 1.3)
- Trailer field = 0xBC
**Parameters:**
- `sig` - Signature bytes (same length as modulus)
- `sig_len` - Signature length in bytes
- `msg_hash` - Pre-computed hash of the message
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm used
- `key` - RSA public key
**Returns:**
- 0 on success (valid signature), -1 on failure
_Symbol type:_ `function`
#### `fio_rsa_sign_pss`
```c
int fio_rsa_sign_pss(uint8_t *signature, size_t *sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_privkey_s *key)
```
Generate an RSA-PSS signature (RSASSA-PSS-SIGN per RFC 8017 Section 8.1.1).
This is required for TLS 1.3 server CertificateVerify messages when using
RSA certificates. PKCS#1 v1.5 signatures are NOT allowed for
CertificateVerify in TLS 1.3.
This implementation uses:
- MGF1 with the same hash function
- Salt length = hash length (as required by TLS 1.3)
- Trailer field = 0xBC
**Parameters:**
- `signature` - Output buffer for signature (must be key->n_len bytes)
- `sig_len` - Output: actual signature length (equals key->n_len)
- `msg_hash` - Pre-computed hash of the message to sign
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm (FIO_RSA_HASH_SHA256, etc.)
- `key` - RSA private key
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/156 mlkem.h`
17 public symbols.
### Macros
#### `FIO_MLKEM768_PUBLICKEYBYTES`
```c
#define FIO_MLKEM768_PUBLICKEYBYTES 1184
```
ML-KEM-768 constants
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SECRETKEYBYTES`
```c
#define FIO_MLKEM768_SECRETKEYBYTES 2400
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_CIPHERTEXTBYTES`
```c
#define FIO_MLKEM768_CIPHERTEXTBYTES 1088
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SSBYTES`
```c
#define FIO_MLKEM768_SSBYTES 32
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SYMBYTES`
```c
#define FIO_MLKEM768_SYMBYTES 32
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_PUBLICKEYBYTES`
```c
#define FIO_X25519MLKEM768_PUBLICKEYBYTES (32 + 1184) /* 1216 */
```
X25519MLKEM768 hybrid constants
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_SECRETKEYBYTES`
```c
#define FIO_X25519MLKEM768_SECRETKEYBYTES (32 + 2400) /* 2432 */
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_CIPHERTEXTBYTES`
```c
#define FIO_X25519MLKEM768_CIPHERTEXTBYTES (32 + 1088) /* 1120 */
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_SSBYTES`
```c
#define FIO_X25519MLKEM768_SSBYTES 64
```
_Symbol type:_ `macro`
### Functions
#### `fio_mlkem768_keypair`
```c
int fio_mlkem768_keypair(uint8_t pk[1184], uint8_t sk[2400])
```
Generate ML-KEM-768 keypair.
Generates a random keypair using the system CSPRNG.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_keypair_derand`
```c
int fio_mlkem768_keypair_derand(uint8_t pk[1184], uint8_t sk[2400], const uint8_t coins[64])
```
Generate ML-KEM-768 keypair from deterministic seed.
The coins buffer must be exactly 64 bytes (d || z).
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_encaps`
```c
int fio_mlkem768_encaps(uint8_t ct[1088], uint8_t ss[32], const uint8_t pk[1184])
```
Encapsulate: generate ciphertext and shared secret from public key.
Uses system CSPRNG for randomness.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_encaps_derand`
```c
int fio_mlkem768_encaps_derand(uint8_t ct[1088], uint8_t ss[32], const uint8_t pk[1184], const uint8_t coins[32])
```
Encapsulate with deterministic randomness.
The coins buffer must be exactly 32 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_decaps`
```c
int fio_mlkem768_decaps(uint8_t ss[32], const uint8_t ct[1088], const uint8_t sk[2400])
```
Decapsulate: recover shared secret from ciphertext and secret key.
Uses implicit rejection: if the ciphertext is invalid, a pseudorandom
shared secret is returned (derived from the secret key and ciphertext)
rather than an error, preventing chosen-ciphertext attacks.
Returns 0 on success (always succeeds for well-formed inputs).
_Symbol type:_ `function`
#### `fio_x25519mlkem768_keypair`
```c
int fio_x25519mlkem768_keypair(uint8_t pk[1216], uint8_t sk[2432])
```
Generate X25519MLKEM768 hybrid keypair.
Generates both X25519 and ML-KEM-768 keypairs using system CSPRNG.
The public key is ML-KEM-768_ek (1184) || X25519_pk (32) = 1216 bytes.
The secret key is ML-KEM-768_dk (2400) || X25519_sk (32) = 2432 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_x25519mlkem768_encaps`
```c
int fio_x25519mlkem768_encaps(uint8_t ct[1120], uint8_t ss[64], const uint8_t pk[1216])
```
X25519MLKEM768 hybrid encapsulation.
Performs both X25519 key exchange and ML-KEM-768 encapsulation.
The ciphertext is ML-KEM-768_ct (1088) || X25519_ephemeral_pk (32) = 1120
bytes. The shared secret is ML-KEM-768_ss (32) || X25519_ss (32) = 64 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_x25519mlkem768_decaps`
```c
int fio_x25519mlkem768_decaps(uint8_t ss[64], const uint8_t ct[1120], const uint8_t sk[2432])
```
X25519MLKEM768 hybrid decapsulation.
Performs both X25519 shared secret derivation and ML-KEM-768 decapsulation.
The shared secret is ML-KEM-768_ss (32) || X25519_ss (32) = 64 bytes.
Returns 0 on success, -1 if X25519 shared secret computation fails
(low-order point). ML-KEM-768 uses implicit rejection for invalid
ciphertexts.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/156 x509.h`
57 public symbols.
### Macros
#### `FIO_X509_OID_SHA256_WITH_RSA`
```c
#define FIO_X509_OID_SHA256_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, [15] = 9}}) /* 1.2.840.113549.1.1.11 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SHA384_WITH_RSA`
```c
#define FIO_X509_OID_SHA384_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, [15] = 9}}) /* 1.2.840.113549.1.1.12 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SHA512_WITH_RSA`
```c
#define FIO_X509_OID_SHA512_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, [15] = 9}}) /* 1.2.840.113549.1.1.13 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_RSA_PSS`
```c
#define FIO_X509_OID_RSA_PSS \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A, [15] = 9}}) /* 1.2.840.113549.1.1.10 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA256`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA256 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, [15] = 8}}) /* 1.2.840.10045.4.3.2 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA384`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA384 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, [15] = 8}}) /* 1.2.840.10045.4.3.3 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA512`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA512 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, [15] = 8}}) /* 1.2.840.10045.4.3.4 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ED25519`
```c
#define FIO_X509_OID_ED25519 \
((fio_u128){.u8 = {0x2B, 0x65, 0x70, [15] = 3}}) /* 1.3.101.112 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ED448`
```c
#define FIO_X509_OID_ED448 \
((fio_u128){.u8 = {0x2B, 0x65, 0x71, [15] = 3}}) /* 1.3.101.113 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_RSA_ENCRYPTION`
```c
#define FIO_X509_OID_RSA_ENCRYPTION \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, [15] = 9}}) /* 1.2.840.113549.1.1.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EC_PUBLIC_KEY`
```c
#define FIO_X509_OID_EC_PUBLIC_KEY \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, [15] = 7}}) /* 1.2.840.10045.2.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP256R1`
```c
#define FIO_X509_OID_SECP256R1 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, [15] = 8}}) /* 1.2.840.10045.3.1.7 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP384R1`
```c
#define FIO_X509_OID_SECP384R1 \
((fio_u128){.u8 = {0x2B, 0x81, 0x04, 0x00, 0x22, [15] = 5}}) /* 1.3.132.0.34 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP521R1`
```c
#define FIO_X509_OID_SECP521R1 \
((fio_u128){.u8 = {0x2B, 0x81, 0x04, 0x00, 0x23, [15] = 5}}) /* 1.3.132.0.35 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_X25519`
```c
#define FIO_X509_OID_X25519 \
((fio_u128){.u8 = {0x2B, 0x65, 0x6E, [15] = 3}}) /* 1.3.101.110 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_X448`
```c
#define FIO_X509_OID_X448 \
((fio_u128){.u8 = {0x2B, 0x65, 0x6F, [15] = 3}}) /* 1.3.101.111 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SUBJECT_KEY_ID`
```c
#define FIO_X509_OID_SUBJECT_KEY_ID \
((fio_u128){.u8 = {0x55, 0x1D, 0x0E, [15] = 3}}) /* 2.5.29.14 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_KEY_USAGE`
```c
#define FIO_X509_OID_KEY_USAGE \
((fio_u128){.u8 = {0x55, 0x1D, 0x0F, [15] = 3}}) /* 2.5.29.15 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SUBJECT_ALT_NAME`
```c
#define FIO_X509_OID_SUBJECT_ALT_NAME \
((fio_u128){.u8 = {0x55, 0x1D, 0x11, [15] = 3}}) /* 2.5.29.17 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_BASIC_CONSTRAINTS`
```c
#define FIO_X509_OID_BASIC_CONSTRAINTS \
((fio_u128){.u8 = {0x55, 0x1D, 0x13, [15] = 3}}) /* 2.5.29.19 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_CRL_DIST_POINTS`
```c
#define FIO_X509_OID_CRL_DIST_POINTS \
((fio_u128){.u8 = {0x55, 0x1D, 0x1F, [15] = 3}}) /* 2.5.29.31 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_CERT_POLICIES`
```c
#define FIO_X509_OID_CERT_POLICIES \
((fio_u128){.u8 = {0x55, 0x1D, 0x20, [15] = 3}}) /* 2.5.29.32 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_AUTH_KEY_ID`
```c
#define FIO_X509_OID_AUTH_KEY_ID \
((fio_u128){.u8 = {0x55, 0x1D, 0x23, [15] = 3}}) /* 2.5.29.35 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EXT_KEY_USAGE`
```c
#define FIO_X509_OID_EXT_KEY_USAGE \
((fio_u128){.u8 = {0x55, 0x1D, 0x25, [15] = 3}}) /* 2.5.29.37 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EKU_SERVER_AUTH`
```c
#define FIO_X509_OID_EKU_SERVER_AUTH \
((fio_u128){.u8 = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, [15] = 8}}) /* 1.3.6.1.5.5.7.3.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EKU_CLIENT_AUTH`
```c
#define FIO_X509_OID_EKU_CLIENT_AUTH \
((fio_u128){.u8 = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, [15] = 8}}) /* 1.3.6.1.5.5.7.3.2 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_COMMON_NAME`
```c
#define FIO_X509_OID_COMMON_NAME \
((fio_u128){.u8 = {0x55, 0x04, 0x03, [15] = 3}}) /* 2.5.4.3 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_COUNTRY`
```c
#define FIO_X509_OID_COUNTRY \
((fio_u128){.u8 = {0x55, 0x04, 0x06, [15] = 3}}) /* 2.5.4.6 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_LOCALITY`
```c
#define FIO_X509_OID_LOCALITY \
((fio_u128){.u8 = {0x55, 0x04, 0x07, [15] = 3}}) /* 2.5.4.7 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_STATE`
```c
#define FIO_X509_OID_STATE \
((fio_u128){.u8 = {0x55, 0x04, 0x08, [15] = 3}}) /* 2.5.4.8 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ORGANIZATION`
```c
#define FIO_X509_OID_ORGANIZATION \
((fio_u128){.u8 = {0x55, 0x04, 0x0A, [15] = 3}}) /* 2.5.4.10 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ORG_UNIT`
```c
#define FIO_X509_OID_ORG_UNIT \
((fio_u128){.u8 = {0x55, 0x04, 0x0B, [15] = 3}}) /* 2.5.4.11 */
```
_Symbol type:_ `macro`
#### `FIO_TLS_CERT_PARSE_ERROR`
```c
#define FIO_TLS_CERT_PARSE_ERROR ((size_t)-1)
```
Error value for fio_tls_parse_certificate_message
_Symbol type:_ `macro`
#### `FIO_X509_MAX_CHAIN_DEPTH`
```c
#define FIO_X509_MAX_CHAIN_DEPTH 10
```
_Symbol type:_ `macro`
### Types
#### `fio_x509_key_algo_e`
```c
typedef enum {
FIO_X509_KEY_UNKNOWN = 0,
FIO_X509_KEY_RSA = 1, /**< RSA (any key size) */
FIO_X509_KEY_ECDSA_P256 = 2, /**< ECDSA with P-256/secp256r1 */
FIO_X509_KEY_ECDSA_P384 = 3, /**< ECDSA with P-384/secp384r1 */
FIO_X509_KEY_ED25519 = 4, /**< Ed25519 (EdDSA) */
} fio_x509_key_algo_e
```
Public key algorithm types
_Symbol type:_ `type`
#### `fio_x509_signature_algo_e`
```c
typedef enum {
FIO_X509_SIGNATURE_UNKNOWN = 0,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA256 = 1, /**< sha256WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PKCS1_SHA384 = 2, /**< sha384WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PKCS1_SHA512 = 3, /**< sha512WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PSS_SHA256 = 4, /**< RSA-PSS with SHA-256 */
FIO_X509_SIGNATURE_RSA_PSS_SHA384 = 5, /**< RSA-PSS with SHA-384 */
FIO_X509_SIGNATURE_RSA_PSS_SHA512 = 6, /**< RSA-PSS with SHA-512 */
FIO_X509_SIGNATURE_ECDSA_SHA256 = 7, /**< ecdsa-with-SHA256 */
FIO_X509_SIGNATURE_ECDSA_SHA384 = 8, /**< ecdsa-with-SHA384 */
FIO_X509_SIGNATURE_ED25519 = 9, /**< Ed25519 */
} fio_x509_signature_algo_e
```
Signature algorithm types
_Symbol type:_ `type`
#### `fio_x509_key_usage_e`
```c
typedef enum {
FIO_X509_KU_DIGITAL_SIGNATURE = 0x0080, /* bit 0 = MSB of byte 0 */
FIO_X509_KU_NON_REPUDIATION = 0x0040, /* bit 1 */
FIO_X509_KU_KEY_ENCIPHERMENT = 0x0020, /* bit 2 */
FIO_X509_KU_DATA_ENCIPHERMENT = 0x0010, /* bit 3 */
FIO_X509_KU_KEY_AGREEMENT = 0x0008, /* bit 4 */
FIO_X509_KU_KEY_CERT_SIGN = 0x0004, /* bit 5 */
FIO_X509_KU_CRL_SIGN = 0x0002, /* bit 6 */
FIO_X509_KU_ENCIPHER_ONLY = 0x0001, /* bit 7 */
FIO_X509_KU_DECIPHER_ONLY = 0x8000, /* bit 8 = MSB of byte 1 */
} fio_x509_key_usage_e
```
Key Usage bit flags (RFC 5280 Section 4.2.1.3)
ASN.1 BIT STRING uses MSB-first bit ordering:
- Bit 0 = MSB of first byte (0x80)
- Bit 1 = 0x40, Bit 2 = 0x20, etc.
- Bits 8+ are in the second byte
_Symbol type:_ `type`
#### `fio_x509_error_e`
```c
typedef enum {
FIO_X509_OK = 0, /**< Validation successful */
FIO_X509_ERR_PARSE = -1, /**< Failed to parse certificate */
FIO_X509_ERR_EXPIRED = -2, /**< Certificate expired */
FIO_X509_ERR_NOT_YET_VALID = -3, /**< Certificate not yet valid */
FIO_X509_ERR_SIGNATURE = -4, /**< Signature verification failed */
FIO_X509_ERR_ISSUER_MISMATCH = -5, /**< Issuer DN doesn't match subject DN */
FIO_X509_ERR_NOT_CA = -6, /**< Issuer is not a CA certificate */
FIO_X509_ERR_NO_TRUST_ANCHOR = -7, /**< Certificate not in trust store */
FIO_X509_ERR_HOSTNAME_MISMATCH = -8, /**< Hostname doesn't match cert */
FIO_X509_ERR_EMPTY_CHAIN = -9, /**< Empty certificate chain */
FIO_X509_ERR_CHAIN_TOO_LONG = -10, /**< Chain exceeds maximum depth */
} fio_x509_error_e
```
X.509 chain validation error codes
_Symbol type:_ `type`
#### `fio_x509_trust_store_s`
```c
typedef struct {
const uint8_t **roots; /**< Array of root CA certificate DER data */
const size_t *root_lens; /**< Array of root CA certificate lengths */
size_t root_count; /**< Number of root CAs */
} fio_x509_trust_store_s
```
Trust store for root CA certificates
_Symbol type:_ `type`
#### `fio_tls_cert_entry_s`
```c
typedef struct {
const uint8_t *cert; /**< DER-encoded certificate data */
size_t cert_len; /**< Certificate length */
} fio_tls_cert_entry_s
```
TLS certificate entry (parsed from Certificate message)
_Symbol type:_ `type`
#### `fio_x509_cert_s`
```c
struct fio_x509_cert_s {
/* Buffer views (16 bytes each, 8-byte aligned — no padding) */
/** Raw DER data (points to the original input, NOT a copy) */
fio_ubuf_info_s der;
/** Certificate serial number (raw INTEGER contents, pointer into DER) */
fio_ubuf_info_s serial;
/** Subject Distinguished Name (raw DER for comparison) */
fio_ubuf_info_s subject;
/** Issuer Distinguished Name (raw DER for comparison) */
fio_ubuf_info_s issuer;
/** Subject Common Name (if present, pointer into DER data) */
fio_buf_info_s cn;
/** Public Key Data (union based on key_algo) */
union {
struct {
fio_ubuf_info_s n; /**< RSA modulus (big-endian) */
fio_ubuf_info_s e; /**< RSA exponent (big-endian) */
} rsa;
struct {
fio_ubuf_info_s point; /**< Uncompressed EC point (04 || x || y) */
} ecdsa;
struct {
fio_ubuf_info_s key; /**< 32-byte Ed25519 public key */
} ed25519;
} pubkey;
/** Signature value (pointer into DER data) */
fio_ubuf_info_s signature;
/** TBS Certificate (for signature verification) */
fio_ubuf_info_s tbs;
/** Subject Alternative Name: first DNS name (if present) */
fio_buf_info_s san_dns;
/** Subject Alternative Name extension raw data (for iterating all SANs) */
fio_ubuf_info_s san_ext;
/** Subject Alternative Name: first IP address (if present) */
fio_ubuf_info_s san_ip;
/** Validity period (Unix timestamps) */
int64_t not_before;
int64_t not_after;
/** SHA-256 fingerprint of the DER data (see fio_x509_fingerprint) */
uint8_t fingerprint[32];
/* Small fields grouped at the end (no bitfields — byte access is faster) */
/** Public Key Type */
fio_x509_key_algo_e key_algo;
/** Signature Algorithm */
fio_x509_signature_algo_e signature_algo;
/** Key Usage extension bits */
uint16_t key_usage;
/** Peer chain verification state: non-zero if a TLS backend verified this
* certificate's chain (see fio_io_peer_info_next). Always zero after
* fio_x509_parse — parsing does not verify anything. */
uint8_t verified;
/** Position in the peer certificate chain (0 = leaf certificate).
* Iteration is capped at 128 certificates (max value 127) as a
* deep-nesting / DoS guard. Set by fio_io_peer_info_next.
* Always zero after fio_x509_parse. */
uint8_t chain_index;
/** Certificate version (0=v1, 1=v2, 2=v3) */
uint8_t version;
/** Basic Constraints: is CA */
uint8_t is_ca;
/** Key Usage extension present */
uint8_t has_key_usage;
}
```
Parsed X.509 certificate structure.
All buffer fields are non-owning views (`fio_buf_info_s` /
`fio_ubuf_info_s`) into the original DER data, which must remain valid
while the certificate is in use.
When a certificate is obtained through fio_io_peer_info_next (TLS peer
certificate inspection), the views point into memory owned by the TLS
backend and remain valid until the next fio_io_peer_info_next call (on ANY
connection) or until the connection is closed, whichever comes first.
_Symbol type:_ `type`
#### `fio_x509_keypair_type_e`
```c
typedef enum {
FIO_X509_KEYPAIR_ED25519 = 1, /**< Ed25519 (preferred) */
FIO_X509_KEYPAIR_P256 = 2, /**< ECDSA P-256 */
} fio_x509_keypair_type_e
```
Key pair types for certificate generation
_Symbol type:_ `type`
#### `fio_x509_keypair_s`
```c
typedef struct {
fio_x509_keypair_type_e type; /**< Key type */
uint8_t secret_key[64]; /**< Secret key (32 bytes for Ed25519, P-256) */
uint8_t public_key[65]; /**< Public key (32 for Ed25519, 65 for P-256) */
size_t secret_key_len; /**< Actual secret key length */
size_t public_key_len; /**< Actual public key length */
} fio_x509_keypair_s
```
Key pair structure for certificate generation
_Symbol type:_ `type`
#### `fio_x509_cert_options_s`
```c
typedef struct {
fio_buf_info_s cn; /**< Subject Common Name (required) */
fio_buf_info_s org; /**< Subject Organization (optional) */
fio_buf_info_s ou; /**< Subject Organizational Unit (optional) */
fio_buf_info_s country; /**< Subject Country (optional, 2 chars) */
int64_t not_before; /**< Validity start (Unix timestamp, 0 = now) */
int64_t not_after; /**< Validity end (Unix timestamp, 0 = +1 year) */
const fio_buf_info_s *san_dns; /**< Subject Alternative Names (DNS) */
size_t san_dns_count; /**< Number of SAN DNS entries */
int is_ca; /**< Set CA:TRUE in BasicConstraints */
uint16_t key_usage; /**< Key Usage bits (0 = default for type) */
} fio_x509_cert_options_s
```
Certificate generation options.
All string fields are `fio_buf_info_s` views (non-owning) — a zero `.len`
marks the field as absent. There is NO implicit strlen fallback: callers
wrapping C strings should use `FIO_BUF_INFO2((char *)s, FIO_STRLEN(s))`.
_Symbol type:_ `type`
### Functions
#### `fio_x509_parse`
```c
int fio_x509_parse(fio_x509_cert_s *cert, const uint8_t *der_data, size_t der_len)
```
Parse a DER-encoded X.509 certificate.
The cert structure will contain pointers into the original DER data,
so the DER data must remain valid while the cert is in use.
**Parameters:**
- `cert` - Output certificate structure (will be zeroed first)
- `der_data` - Pointer to DER-encoded certificate
- `der_len` - Length of DER data in bytes
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_fingerprint`
```c
void fio_x509_fingerprint(fio_x509_cert_s *cert)
```
Computes the SHA-256 fingerprint of the certificate's DER data, storing
the raw 32-byte hash in `cert->fingerprint`.
Call after fio_x509_parse. This is a lazy (on-demand) operation so that
parsing alone never pays the hashing cost.
**Parameters:**
- `cert` - Parsed certificate (must have been filled by fio_x509_parse)
_Symbol type:_ `function`
#### `fio_x509_verify_signature`
```c
int fio_x509_verify_signature(const fio_x509_cert_s *cert, const fio_x509_cert_s *issuer)
```
Verify certificate signature using issuer's public key.
This verifies that the certificate was signed by the issuer.
**Parameters:**
- `cert` - Certificate to verify
- `issuer` - Certificate of the issuer (contains the public key)
**Returns:**
- 0 if valid, -1 if invalid or error
_Symbol type:_ `function`
#### `fio_x509_check_validity`
```c
inline int fio_x509_check_validity(const fio_x509_cert_s *cert, int64_t current_time)
```
Check if certificate is currently valid (not expired, not yet valid).
**Parameters:**
- `cert` - Certificate to check
- `current_time` - Current Unix timestamp (seconds since epoch)
**Returns:**
- 0 if valid, -1 if expired or not yet valid
_Symbol type:_ `function`
#### `fio_x509_match_hostname`
```c
int fio_x509_match_hostname(const fio_x509_cert_s *cert, const char *hostname, size_t hostname_len)
```
Check if hostname matches certificate (CN or SAN).
Supports wildcard matching (*.example.com).
Per RFC 6125, wildcards only match one label.
**Parameters:**
- `cert` - Certificate to check
- `hostname` - Hostname to match
- `hostname_len` - Length of hostname
**Returns:**
- 0 if match, -1 if no match
_Symbol type:_ `function`
#### `fio_x509_verify_chain`
```c
int fio_x509_verify_chain(const fio_ubuf_info_s *certs, size_t cert_count, const char *hostname, int64_t current_time, fio_x509_trust_store_s *trust_store)
```
Validate a certificate chain for TLS 1.3.
The chain should be ordered from end-entity to closest-to-root:
- certs[0] = server's certificate (end-entity)
- certs[1] = intermediate CA (signed certs[0])
- certs[n-1] = closest to root (may be root or intermediate)
Validation performs:
1. Parse all certificates
2. Check validity period for all certificates
3. Verify hostname matches end-entity certificate (if hostname provided)
4. Verify each certificate's signature using the next certificate's key
5. Verify issuer DNs match subject DNs in the chain
6. Verify intermediate/root certificates have CA:TRUE
7. Verify the chain terminates at a trusted root (if trust store provided)
**Parameters:**
- `certs` - Array of DER-encoded certificate views
- `cert_count` - Number of certificates in chain
- `hostname` - Expected hostname for end-entity (NULL to skip check)
- `current_time` - Current Unix timestamp for validity checking
- `trust_store` - Root CA certificates (NULL to skip trust check)
**Returns:**
- FIO_X509_OK (0) on success, or error code on failure
_Symbol type:_ `function`
#### `fio_x509_is_trusted`
```c
int fio_x509_is_trusted(const fio_x509_cert_s *cert, fio_x509_trust_store_s *trust_store)
```
Check if a certificate is in the trust store.
Comparison is done by matching subject DN.
**Parameters:**
- `cert` - Certificate to check
- `trust_store` - Trust store to search
**Returns:**
- 0 if trusted, -1 if not found
_Symbol type:_ `function`
#### `fio_tls_parse_certificate_message`
```c
size_t fio_tls_parse_certificate_message(fio_tls_cert_entry_s *entries, size_t max_entries, const uint8_t *data, size_t data_len)
```
Parse TLS 1.3 Certificate message into individual certificates.
TLS 1.3 Certificate message format (RFC 8446):
certificate_request_context<0..2^8-1>
certificate_list<0..2^24-1>:
CertificateEntry:
cert_data<1..2^24-1>
extensions<0..2^16-1>
**Parameters:**
- `entries` - Output array for certificate entries
- `max_entries` - Maximum entries to parse
- `data` - Raw Certificate message data (after handshake header)
- `data_len` - Length of Certificate message data
**Returns:**
- Number of certificates parsed, or FIO_TLS_CERT_PARSE_ERROR on error
_Symbol type:_ `function`
#### `fio_x509_error_str`
```c
inline const char *fio_x509_error_str(int error)
```
Get human-readable error string for X.509 validation error code.
**Parameters:**
- `error` - Error code from fio_x509_verify_chain
**Returns:**
- Static string describing the error
_Symbol type:_ `function`
#### `fio_x509_keypair_ed25519`
```c
int fio_x509_keypair_ed25519(fio_x509_keypair_s *keypair)
```
Generate an Ed25519 key pair for certificate signing.
**Parameters:**
- `keypair` - Output key pair structure
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_keypair_p256`
```c
int fio_x509_keypair_p256(fio_x509_keypair_s *keypair)
```
Generate a P-256 key pair for certificate signing.
**Parameters:**
- `keypair` - Output key pair structure
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_self_signed_cert`
```c
size_t fio_x509_self_signed_cert(uint8_t *buf, size_t buf_len, const fio_x509_keypair_s *keypair, const fio_x509_cert_options_s *options)
```
Generate a self-signed X.509v3 certificate.
The certificate is DER-encoded and written to the output buffer.
Call with buf=NULL to get the maximum possible certificate size.
This returns a worst-case size that accounts for variable-length DER
encoding (e.g., serial numbers with varying leading zeros), ensuring
that a buffer of this size will always be sufficient for the actual
generation call.
**Parameters:**
- `buf` - Output buffer (can be NULL to calculate max size)
- `buf_len` - Buffer size (ignored if buf is NULL)
- `keypair` - Key pair to use for signing
- `options` - Certificate options
**Returns:**
- Number of bytes written (if buf!=NULL) or max needed (if buf==NULL),
or 0 on error
_Symbol type:_ `function`
#### `fio_x509_keypair_clear`
```c
inline void fio_x509_keypair_clear(fio_x509_keypair_s *keypair)
```
Securely clear a key pair structure.
**Parameters:**
- `keypair` - Key pair to clear
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/157 pem.h`
8 public symbols.
### Types
#### `fio_pem_key_type_e`
```c
typedef enum {
FIO_PEM_KEY_UNKNOWN = 0,
FIO_PEM_KEY_RSA = 1,
FIO_PEM_KEY_ECDSA_P256 = 2,
FIO_PEM_KEY_ED25519 = 3,
} fio_pem_key_type_e
```
Private key algorithm types
_Symbol type:_ `type`
#### `fio_pem_s`
```c
typedef struct {
const uint8_t *der; /**< Pointer to decoded DER data */
size_t der_len; /**< Length of DER data */
const char *label; /**< PEM label (e.g., "CERTIFICATE", "PRIVATE KEY") */
size_t label_len; /**< Length of label */
} fio_pem_s
```
Parsed PEM block
_Symbol type:_ `type`
#### `fio_pem_private_key_s`
```c
typedef struct {
fio_pem_key_type_e type;
union {
struct {
uint8_t n[FIO_RSA_MAX_BYTES]; /**< RSA modulus (big-endian) */
size_t n_len;
uint8_t e[FIO_RSA_MAX_BYTES]; /**< RSA public exponent (big-endian) */
size_t e_len;
uint8_t d[FIO_RSA_MAX_BYTES]; /**< RSA private exponent (big-endian) */
size_t d_len;
uint8_t p[FIO_RSA_MAX_BYTES]; /**< RSA prime p (optional) */
size_t p_len;
uint8_t q[FIO_RSA_MAX_BYTES]; /**< RSA prime q (optional) */
size_t q_len;
uint8_t dP[FIO_RSA_MAX_BYTES]; /**< d mod (p-1) (optional) */
size_t dP_len;
uint8_t dQ[FIO_RSA_MAX_BYTES]; /**< d mod (q-1) (optional) */
size_t dQ_len;
uint8_t qInv[FIO_RSA_MAX_BYTES]; /**< q^-1 mod p (optional) */
size_t qInv_len;
} rsa;
struct {
uint8_t private_key[32]; /**< P-256 scalar (32 bytes) */
uint8_t public_key[65]; /**< Uncompressed point (optional, can derive) */
int has_public_key; /**< 1 if public_key is populated */
} ecdsa_p256;
struct {
uint8_t private_key[32]; /**< Ed25519 seed (32 bytes) */
uint8_t public_key[32]; /**< Ed25519 public key (optional) */
int has_public_key; /**< 1 if public_key is populated */
} ed25519;
};
} fio_pem_private_key_s
```
Parsed private key structure
_Symbol type:_ `type`
### Functions
#### `fio_pem_parse`
```c
size_t fio_pem_parse(fio_pem_s *out, uint8_t *der_buf, size_t der_buf_len, const char *pem_data, size_t pem_len)
```
Parse a single PEM block from data.
Finds the next -----BEGIN ----- and -----END ----- markers,
base64 decodes the content between them, and returns the DER data.
**Parameters:**
- `out` - Output structure to fill with parsed PEM block info
- `der_buf` - Buffer to store decoded DER data (caller-provided)
- `der_buf_len` - Size of der_buf
- `pem_data` - PEM-encoded data
- `pem_len` - Length of PEM data
**Returns:**
- Number of bytes consumed from pem_data, or 0 on error
_Symbol type:_ `function`
#### `fio_pem_parse_certificate`
```c
int fio_pem_parse_certificate(fio_x509_cert_s *cert, const char *pem_data, size_t pem_len)
```
Parse certificate from PEM file content.
Handles "CERTIFICATE" label and parses the X.509 certificate.
**Parameters:**
- `cert` - Output certificate structure (from fio_x509.h)
- `pem_data` - PEM-encoded certificate data
- `pem_len` - Length of PEM data
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_pem_parse_private_key`
```c
int fio_pem_parse_private_key(fio_pem_private_key_s *key, const char *pem_data, size_t pem_len)
```
Parse private key from PEM file content.
Supports:
- "PRIVATE KEY" (PKCS#8 PrivateKeyInfo)
- "RSA PRIVATE KEY" (PKCS#1 RSAPrivateKey)
- "EC PRIVATE KEY" (SEC1 ECPrivateKey)
**Parameters:**
- `key` - Output private key structure
- `pem_data` - PEM-encoded private key data
- `pem_len` - Length of PEM data
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_pem_get_certificate_der`
```c
size_t fio_pem_get_certificate_der(uint8_t *der_out, size_t der_out_len, const char *pem_data, size_t pem_len)
```
Get the DER-encoded certificate from PEM data.
This is a convenience function that extracts just the DER bytes
without parsing the X.509 structure.
**Parameters:**
- `der_out` - Output buffer for DER data
- `der_out_len` - Size of output buffer
- `pem_data` - PEM-encoded certificate data
- `pem_len` - Length of PEM data
**Returns:**
- Length of DER data written, or 0 on error
_Symbol type:_ `function`
#### `fio_pem_private_key_clear`
```c
inline void fio_pem_private_key_clear(fio_pem_private_key_s *key)
```
Securely clear a private key structure.
**Parameters:**
- `key` - Private key to clear
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/159 argon2.h`
6 public symbols.
### Types
#### `fio_argon2_type_e`
```c
typedef enum {
FIO_ARGON2D = 0,
FIO_ARGON2I = 1,
FIO_ARGON2ID = 2,
} fio_argon2_type_e
```
Argon2 type selector.
_Symbol type:_ `type`
#### `fio_argon2_args_s`
```c
typedef struct {
/** The password (message P). */
fio_buf_info_s password;
/** The salt (nonce S). */
fio_buf_info_s salt;
/** Optional secret key K. */
fio_buf_info_s secret;
/** Optional associated data X. */
fio_buf_info_s ad;
/** Time cost (number of passes t, minimum 1). */
uint32_t t_cost;
/** Memory cost in KiB (minimum 8*parallelism). */
uint32_t m_cost;
/** Degree of parallelism (number of lanes, minimum 1). */
uint32_t parallelism;
/** Desired output (tag) length in bytes (minimum 4, default 32). */
uint32_t outlen;
/** Argon2 variant: FIO_ARGON2D, FIO_ARGON2I, or FIO_ARGON2ID. */
fio_argon2_type_e type;
} fio_argon2_args_s
```
Argon2 named arguments.
_Symbol type:_ `type`
### Functions
#### `fio_argon2`
```c
fio_u512 fio_argon2(fio_argon2_args_s args)
```
Computes Argon2 password hash (RFC 9106).
Supports Argon2d, Argon2i, and Argon2id variants.
Output is written to the returned buffer (up to 64 bytes via fio_u512).
For outlen > 64, use fio_argon2_hash() instead.
Use `fio_ct_is_eq` to compare output hashes in constant time.
_Symbol type:_ `function`
#### `fio_argon2`
```c
#define fio_argon2(...) fio_argon2((fio_argon2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_argon2_hash`
```c
int fio_argon2_hash(void *out, fio_argon2_args_s args)
```
Computes Argon2 password hash into a caller-provided buffer.
Supports arbitrary output lengths (minimum 4 bytes).
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_argon2_hash`
```c
#define fio_argon2_hash(out, ...) \
fio_argon2_hash(out, (fio_argon2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 lyra2.h`
5 public symbols.
### Types
#### `fio_lyra2_args_s`
```c
typedef struct {
/** The password to hash. */
fio_buf_info_s password;
/** The salt for the hash. */
fio_buf_info_s salt;
/** Time cost (number of rounds, minimum 1). */
uint64_t t_cost;
/** Memory cost (number of rows in the matrix, minimum 3). */
uint64_t m_cost;
/** Desired output length in bytes (default 32 if 0). */
size_t outlen;
/** Number of columns (default 256 if 0). */
size_t n_cols;
} fio_lyra2_args_s
```
Lyra2 named arguments.
_Symbol type:_ `type`
### Functions
#### `fio_lyra2`
```c
fio_u512 fio_lyra2(fio_lyra2_args_s args)
```
Computes Lyra2 password hash.
Uses Blake2b as the underlying sponge/hash function.
Matches the reference C implementation (nPARALLEL==1, SPONGE==0, RHO==1).
Output is written to the returned buffer (up to 512 bytes via fio_u512).
For outlen > 64, use fio_lyra2_hash() instead.
Use `fio_ct_is_eq` to compare output hashes in constant time.
_Symbol type:_ `function`
#### `fio_lyra2`
```c
#define fio_lyra2(...) fio_lyra2((fio_lyra2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lyra2_hash`
```c
int fio_lyra2_hash(void *out, fio_lyra2_args_s args)
```
Computes Lyra2 password hash into a caller-provided buffer.
Supports arbitrary output lengths.
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_lyra2_hash`
```c
#define fio_lyra2_hash(out, ...) \
fio_lyra2_hash(out, (fio_lyra2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 otp.h`
7 public symbols.
### Types
#### `fio_otp_settings_s`
```c
typedef struct {
/** The time interval for TOTP rotation. */
size_t interval; /* 30 == Google OTP */
/** The number of digits in the OTP. */
size_t digits; /* 6 == Google OTP */
/** The time offset (in `interval` units) from the current time. */
int64_t offset; /* 0 == Google OTP */
/** Set to true if the secret / key is in Hex instead of Byte32 encoding. */
uint8_t is_hex;
/** Set to true if the secret / key is raw bit data (no encoding). */
uint8_t is_raw;
} fio_otp_settings_s
```
_Symbol type:_ `type`
### Functions
#### `fio_otp_generate_key`
```c
inline fio_u128 fio_otp_generate_key(void)
```
Generates a random 128 bit key for TOTP processing.
_Symbol type:_ `function`
#### `fio_otp_print_key`
```c
inline size_t fio_otp_print_key(char *dest, uint8_t *key, size_t len)
```
Prints out an OTP secret (big endian number) as a Byte32 encoded String.
_Symbol type:_ `function`
#### `fio_otp`
```c
uint32_t fio_otp(fio_buf_info_s secret, fio_otp_settings_s settings)
```
Returns a TOTP based on `secret` and the otp settings.
_Symbol type:_ `function`
#### `fio_otp`
```c
#define fio_otp(secret, ...) fio_otp(secret, (fio_otp_settings_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_otp_at`
```c
uint32_t fio_otp_at(fio_buf_info_s secret, uint64_t unix_time, fio_otp_settings_s settings)
```
Returns a TOTP for a specific unix timestamp (for testing/verification).
This is useful for verifying OTPs at specific times or for RFC test vectors.
_Symbol type:_ `function`
#### `fio_otp_at`
```c
#define fio_otp_at(secret, unix_time, ...) \
fio_otp_at(secret, unix_time, (fio_otp_settings_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 secret.h`
5 public symbols.
### Functions
#### `fio_secret_is_random`
```c
bool fio_secret_is_random(void)
```
Returns true if the secret was randomly generated.
_Symbol type:_ `function`
#### `fio_secret`
```c
fio_u512 fio_secret(void)
```
Gets the SHA512 of a (possibly shared) secret.
_Symbol type:_ `function`
#### `fio_secret_set`
```c
void fio_secret_set(char *str, size_t len, bool is_random)
```
Sets a (possibly shared) secret and stores its SHA512 hash.
_Symbol type:_ `function`
#### `fio_secret_set_at`
```c
void fio_secret_set_at(fio_u512 *secret, char *str, size_t len)
```
Sets a (possibly shared) secret and stores its SHA512 hash.
_Symbol type:_ `function`
#### `fio_secret_at`
```c
fio_u512 fio_secret_at(fio_u512 *secret)
```
Gets the SHA512 of a (possibly shared) secret.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/162 brotli.h`
4 public symbols.
### Functions
#### `fio_brotli_decompress`
```c
size_t fio_brotli_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses Brotli-compressed data (RFC 7932).
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: performs a header scan summing MLEN
values and returns the required size (0 if data is corrupt).
This allows callers to query the decompressed size without allocating.
_Symbol type:_ `function`
#### `fio_brotli_decompress_bound`
```c
inline size_t fio_brotli_decompress_bound(size_t in_len)
```
Conservative upper bound on decompressed size for a Brotli stream.
_Symbol type:_ `function`
#### `fio_brotli_compress`
```c
size_t fio_brotli_compress(void *out, size_t out_len, const void *in, size_t in_len, int quality)
```
Compresses data using Brotli (RFC 7932).
Returns compressed length on success, 0 on error.
quality: 1-6 (1=fast greedy, 3-4=lazy matching, 5-6=hash chain + context
modeling + static dictionary). Caller provides output buffer sized via
fio_brotli_compress_bound().
_Symbol type:_ `function`
#### `fio_brotli_compress_bound`
```c
inline size_t fio_brotli_compress_bound(size_t in_len)
```
Upper bound on Brotli compressed output size.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/162 deflate.h`
13 public symbols.
### Types
#### `fio_deflate_s`
```c
struct fio_deflate_s
```
Opaque streaming deflate/inflate state.
_Symbol type:_ `type`
### Functions
#### `fio_deflate_decompress`
```c
size_t fio_deflate_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses raw DEFLATE data (no zlib/gzip headers).
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: performs a full decode pass counting
output bytes and returns the required size (0 if data is corrupt).
This allows callers to query the decompressed size without allocating.
_Symbol type:_ `function`
#### `fio_deflate_decompress_bound`
```c
inline size_t fio_deflate_decompress_bound(size_t in_len)
```
Conservative upper bound on decompressed size.
_Symbol type:_ `function`
#### `fio_deflate_compress`
```c
size_t fio_deflate_compress(void *out, size_t out_len, const void *in, size_t in_len, int level)
```
Compresses data using raw DEFLATE (no zlib/gzip headers).
Returns compressed length on success, 0 on error.
level: 0=store, 1-3=fast, 4-6=normal, 7-9=best compression.
_Symbol type:_ `function`
#### `fio_deflate_compress_bound`
```c
inline size_t fio_deflate_compress_bound(size_t in_len)
```
Upper bound on compressed output size.
_Symbol type:_ `function`
#### `fio_gzip_compress`
```c
size_t fio_gzip_compress(void *out, size_t out_len, const void *in, size_t in_len, int level)
```
Compresses data with gzip wrapper (for HTTP Content-Encoding).
Returns total output length on success, 0 on error.
_Symbol type:_ `function`
#### `fio_gzip_decompress`
```c
size_t fio_gzip_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses gzip data.
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: returns required size (0 if corrupt).
_Symbol type:_ `function`
#### `fio_deflate_new`
```c
fio_deflate_s *fio_deflate_new(int level, int is_compress)
```
Creates a new streaming deflate/inflate state.
`level`: compression level 1-9 (ignored for decompression).
`is_compress`: non-zero for compression, 0 for decompression.
Returns NULL on allocation failure.
_Symbol type:_ `function`
#### `fio_deflate_new_takeover`
```c
fio_deflate_s *fio_deflate_new_takeover(int level, int is_compress)
```
Creates a streaming state with context takeover (cross-message history
over the last 32KB). Allocates the window (+ compressor hash) inside the
context's own block — the documented per-context cost (~160KB compressor,
~32KB decompressor). `fio_deflate_new` (no-takeover) is the cheap default
and the only mode the WebSocket layer negotiates.
_Symbol type:_ `function`
#### `fio_deflate_free`
```c
void fio_deflate_free(fio_deflate_s *s)
```
Frees a streaming deflate/inflate state.
_Symbol type:_ `function`
#### `fio_deflate_destroy`
```c
void fio_deflate_destroy(fio_deflate_s *s)
```
Resets a deflate streaming context (keeps allocated memory, clears state).
Frees the input buffer when it grew past 64KB, keeping persistent
per-connection state bounded (no-takeover design).
_Symbol type:_ `function`
#### `fio_deflate_window_bits_set`
```c
void fio_deflate_window_bits_set(fio_deflate_s *s, int bits)
```
Clamps compressor match distances to 2^bits (8..15, default 15).
Used to honor `server_max_window_bits` from RFC 7692 negotiation.
_Symbol type:_ `function`
#### `fio_deflate_push`
```c
size_t fio_deflate_push(fio_deflate_s *s, void *out, size_t out_len, const void *in, size_t in_len, int flush)
```
Streaming compress/decompress (no-takeover: each flushed message is an
independent deflate stream — there is no cross-message history mode).
Processes `in_len` bytes from `in`, writing output to `out` (max `out_len`).
`flush`: 0=normal, 1=sync_flush (for WebSocket frame boundaries).
Decompression returns:
- Decompressed byte count on success (<= out_len).
- Required buffer size when buffer too small (> out_len). Internal buffer is
preserved for retry with a larger output buffer.
- 0 on corrupt/invalid data or allocation failure.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/190 tls13.h`
60 public symbols.
### Macros
#### `FIO_TLS13_SHA256_HASH_LEN`
```c
#define FIO_TLS13_SHA256_HASH_LEN 32
```
SHA-256 hash length (32 bytes).
_Symbol type:_ `macro`
#### `FIO_TLS13_SHA384_HASH_LEN`
```c
#define FIO_TLS13_SHA384_HASH_LEN 48
```
SHA-384 hash length (48 bytes).
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_HASH_LEN`
```c
#define FIO_TLS13_MAX_HASH_LEN 48
```
Maximum hash length supported.
_Symbol type:_ `macro`
#### `FIO_TLS13_AES128_KEY_LEN`
```c
#define FIO_TLS13_AES128_KEY_LEN 16
```
AES-128-GCM key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_AES256_KEY_LEN`
```c
#define FIO_TLS13_AES256_KEY_LEN 32
```
AES-256-GCM key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_CHACHA_KEY_LEN`
```c
#define FIO_TLS13_CHACHA_KEY_LEN 32
```
ChaCha20-Poly1305 key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_IV_LEN`
```c
#define FIO_TLS13_IV_LEN 12
```
IV length for all AEAD ciphers.
_Symbol type:_ `macro`
#### `FIO_TLS13_RECORD_HEADER_LEN`
```c
#define FIO_TLS13_RECORD_HEADER_LEN 5
```
TLS record header length (5 bytes)
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_PLAINTEXT_LEN`
```c
#define FIO_TLS13_MAX_PLAINTEXT_LEN 16384
```
Maximum plaintext fragment length (2^14 = 16384 bytes)
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_CIPHERTEXT_LEN`
```c
#define FIO_TLS13_MAX_CIPHERTEXT_LEN (16384 + 256)
```
Maximum ciphertext length (plaintext + padding + tag)
_Symbol type:_ `macro`
#### `FIO_TLS13_TAG_LEN`
```c
#define FIO_TLS13_TAG_LEN 16
```
AEAD authentication tag length (16 bytes for all TLS 1.3 ciphers)
_Symbol type:_ `macro`
#### `FIO_TLS13_LEGACY_VERSION_MAJOR`
```c
#define FIO_TLS13_LEGACY_VERSION_MAJOR 0x03
```
Legacy TLS version bytes (0x0303 = TLS 1.2)
_Symbol type:_ `macro`
#### `FIO_TLS13_LEGACY_VERSION_MINOR`
```c
#define FIO_TLS13_LEGACY_VERSION_MINOR 0x03
```
_Symbol type:_ `macro`
#### `FIO_TLS13_VERSION_TLS12`
```c
#define FIO_TLS13_VERSION_TLS12 0x0303
```
TLS 1.3 Protocol Version Constants
_Symbol type:_ `macro`
#### `FIO_TLS13_VERSION_TLS13`
```c
#define FIO_TLS13_VERSION_TLS13 0x0304
```
_Symbol type:_ `macro`
#### `FIO_TLS13_HRR_RANDOM`
```c
#define FIO_TLS13_HRR_RANDOM \
"\xCF\x21\xAD\x74\xE5\x9A\x61\x11\xBE\x1D\x8C\x02\x1E\x65\xB8\x91" \
"\xC2\xA2\x11\x16\x7A\xBB\x8C\x5E\x07\x9E\x09\xE2\xC8\xA8\x33\x9C"
```
HelloRetryRequest magic random value (RFC 8446 Section 4.1.3)
_Symbol type:_ `macro`
### Types
#### `fio_tls13_content_type_e`
```c
typedef enum {
FIO_TLS13_CONTENT_INVALID = 0,
FIO_TLS13_CONTENT_CHANGE_CIPHER_SPEC = 20, /* Legacy, ignored in TLS 1.3 */
FIO_TLS13_CONTENT_ALERT = 21,
FIO_TLS13_CONTENT_HANDSHAKE = 22,
FIO_TLS13_CONTENT_APPLICATION_DATA = 23,
} fio_tls13_content_type_e
```
TLS 1.3 content types
_Symbol type:_ `type`
#### `fio_tls13_handshake_type_e`
```c
typedef enum {
FIO_TLS13_HS_CLIENT_HELLO = 1,
FIO_TLS13_HS_SERVER_HELLO = 2,
FIO_TLS13_HS_NEW_SESSION_TICKET = 4,
FIO_TLS13_HS_END_OF_EARLY_DATA = 5,
FIO_TLS13_HS_ENCRYPTED_EXTENSIONS = 8,
FIO_TLS13_HS_CERTIFICATE = 11,
FIO_TLS13_HS_CERTIFICATE_REQUEST = 13,
FIO_TLS13_HS_CERTIFICATE_VERIFY = 15,
FIO_TLS13_HS_FINISHED = 20,
FIO_TLS13_HS_KEY_UPDATE = 24,
FIO_TLS13_HS_MESSAGE_HASH = 254,
} fio_tls13_handshake_type_e
```
TLS 1.3 Handshake Message Types
_Symbol type:_ `type`
#### `fio_tls13_key_update_request_e`
```c
typedef enum {
FIO_TLS13_KEY_UPDATE_NOT_REQUESTED = 0,
FIO_TLS13_KEY_UPDATE_REQUESTED = 1,
} fio_tls13_key_update_request_e
```
TLS 1.3 KeyUpdate Request Types (RFC 8446 Section 4.6.3)
_Symbol type:_ `type`
#### `fio_tls13_extension_type_e`
```c
typedef enum {
FIO_TLS13_EXT_SERVER_NAME = 0, /* SNI */
FIO_TLS13_EXT_SUPPORTED_GROUPS = 10, /* Key exchange groups */
FIO_TLS13_EXT_SIGNATURE_ALGORITHMS = 13, /* Signature schemes */
FIO_TLS13_EXT_ALPN = 16, /* Application-Layer Protocol Negotiation */
FIO_TLS13_EXT_SUPPORTED_VERSIONS = 43, /* TLS version negotiation */
FIO_TLS13_EXT_COOKIE = 44, /* Cookie for HRR (RFC 8446 4.2.2) */
FIO_TLS13_EXT_CERTIFICATE_AUTHORITIES =
47, /* Acceptable CAs (RFC 8446 4.2.4) */
FIO_TLS13_EXT_SIGNATURE_ALGORITHMS_CERT = 50, /* Cert chain sig algs */
FIO_TLS13_EXT_KEY_SHARE = 51, /* ECDHE key shares */
} fio_tls13_extension_type_e
```
TLS 1.3 Extension Types (RFC 8446 Section 4.2)
_Symbol type:_ `type`
#### `fio_tls13_cipher_suite_e`
```c
typedef enum {
FIO_TLS13_CIPHER_SUITE_AES_128_GCM_SHA256 = 0x1301,
FIO_TLS13_CIPHER_SUITE_AES_256_GCM_SHA384 = 0x1302,
FIO_TLS13_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 = 0x1303,
} fio_tls13_cipher_suite_e
```
TLS 1.3 Cipher Suites (RFC 8446 Section B.4)
_Symbol type:_ `type`
#### `fio_tls13_named_group_e`
```c
typedef enum {
FIO_TLS13_GROUP_SECP256R1 = 23, /* P-256 */
FIO_TLS13_GROUP_SECP384R1 = 24, /* P-384 */
FIO_TLS13_GROUP_X25519 = 29, /* Curve25519 */
FIO_TLS13_GROUP_X25519MLKEM768 =
0x11ec, /* X25519 + ML-KEM-768 hybrid (PQC) */
} fio_tls13_named_group_e
```
TLS 1.3 Named Groups (RFC 8446 Section 4.2.7, draft-ietf-tls-hybrid-design)
_Symbol type:_ `type`
#### `fio_tls13_signature_algo_e`
```c
typedef enum {
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA256 = 0x0401,
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA384 = 0x0501,
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA512 = 0x0601,
FIO_TLS13_SIGNATURE_ECDSA_SECP256R1_SHA256 = 0x0403,
FIO_TLS13_SIGNATURE_ECDSA_SECP384R1_SHA384 = 0x0503,
FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA256 = 0x0804,
FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA384 = 0x0805,
FIO_TLS13_SIGNATURE_ED25519 = 0x0807,
} fio_tls13_signature_algo_e
```
TLS 1.3 Signature Algorithms (RFC 8446 Section 4.2.3)
_Symbol type:_ `type`
#### `fio_tls13_server_hello_s`
```c
typedef struct {
uint8_t random[32]; /* Server random */
uint16_t cipher_suite; /* Selected cipher suite */
uint8_t
key_share[1120]; /* Server's key share (max size for X25519MLKEM768) */
uint16_t key_share_len; /* Length of key share */
uint16_t key_share_group; /* Selected group */
int is_hello_retry_request; /* 1 if HRR */
} fio_tls13_server_hello_s
```
Parsed ServerHello message
_Symbol type:_ `type`
#### `fio_tls13_encrypted_extensions_s`
```c
typedef struct {
int has_server_name; /* Server acknowledged SNI */
char alpn_selected[256]; /* Selected ALPN protocol (null-terminated) */
size_t alpn_selected_len; /* Length of selected ALPN protocol */
} fio_tls13_encrypted_extensions_s
```
Parsed EncryptedExtensions message
_Symbol type:_ `type`
#### `fio_tls13_certificate_s`
```c
typedef struct {
fio_ubuf_info_s cert; /* First certificate (view into message data) */
} fio_tls13_certificate_s
```
Parsed Certificate message (minimal - first cert only)
_Symbol type:_ `type`
#### `fio_tls13_certificate_verify_s`
```c
typedef struct {
fio_ubuf_info_s signature; /* Signature (view into message data) */
uint16_t signature_algo;
} fio_tls13_certificate_verify_s
```
Parsed CertificateVerify message
_Symbol type:_ `type`
#### `fio_tls13_certificate_request_s`
```c
typedef struct {
fio_ubuf_info_s authorities; /* Raw CA DNs data (view into message) */
size_t context_len; /* Context length (0-255) */
size_t algo_count; /* Number of signature algorithms */
size_t cert_algos_count; /* Number of cert sig algos */
uint16_t algos[16]; /* Required signature algorithms */
uint16_t cert_algos[16]; /* Cert chain sig algos (optional) */
uint8_t context[255]; /* Opaque context */
} fio_tls13_certificate_request_s
```
Parsed CertificateRequest message (RFC 8446 Section 4.3.2)
_Symbol type:_ `type`
#### `fio_tls13_cipher_type_e`
```c
typedef enum {
FIO_TLS13_CIPHER_AES_128_GCM = 0, /* TLS_AES_128_GCM_SHA256 */
FIO_TLS13_CIPHER_AES_256_GCM = 1, /* TLS_AES_256_GCM_SHA384 */
FIO_TLS13_CIPHER_CHACHA20_POLY1305 = 2, /* TLS_CHACHA20_POLY1305_SHA256 */
} fio_tls13_cipher_type_e
```
Supported AEAD cipher types for TLS 1.3
_Symbol type:_ `type`
#### `fio_tls13_plaintext_header_s`
```c
typedef struct {
uint8_t content_type; /* ContentType */
uint8_t legacy_version[2]; /* 0x03, 0x03 (TLS 1.2) */
uint16_t length; /* Fragment length (big-endian) */
/* Fragment follows (up to 2^14 bytes) */
} fio_tls13_plaintext_header_s
```
TLSPlaintext header structure (RFC 8446 Section 5.1)
_Symbol type:_ `type`
#### `fio_tls13_ciphertext_header_s`
```c
typedef struct {
uint8_t opaque_type; /* Always 23 (application_data) */
uint8_t legacy_version[2]; /* 0x03, 0x03 */
uint16_t length; /* Encrypted length + tag (big-endian) */
/* Encrypted content follows */
} fio_tls13_ciphertext_header_s
```
TLSCiphertext header structure (RFC 8446 Section 5.2)
_Symbol type:_ `type`
#### `fio_tls13_record_keys_s`
```c
typedef struct {
uint8_t key[32]; /* Write key (16 or 32 bytes depending on cipher) */
uint8_t iv[12]; /* Write IV (always 12 bytes) */
uint64_t sequence_number; /* Per-record sequence number (starts at 0) */
uint8_t key_len; /* 16 for AES-128, 32 for AES-256/ChaCha20 */
uint8_t cipher_type; /* fio_tls13_cipher_type_e */
} fio_tls13_record_keys_s
```
Record encryption context (per-direction keys)
_Symbol type:_ `type`
### Functions
#### `fio_tls13_hkdf_expand_label`
```c
void fio_tls13_hkdf_expand_label(void *restrict out, size_t out_len, const void *restrict secret, size_t secret_len, const char *label, size_t label_len, const void *restrict context, size_t context_len, int use_sha384)
```
TLS 1.3 HKDF-Expand-Label function.
Derives keying material using the TLS 1.3 specific label format.
**Parameters:**
- `out` - Output buffer for derived key material
- `out_len` - Desired output length (max 255)
- `secret` - The secret to expand (PRK from HKDF-Extract)
- `secret_len` - Secret length (32 for SHA-256, 48 for SHA-384)
- `label` - The label string (without "tls13 " prefix)
- `label_len` - Label length (max 249 to fit in 255 with prefix)
- `context` - Optional context (transcript hash or empty)
- `context_len` - Context length (max 255)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_secret`
```c
void fio_tls13_derive_secret(void *restrict out, const void *restrict secret, size_t secret_len, const char *label, size_t label_len, const void *restrict transcript_hash, size_t hash_len, int use_sha384)
```
TLS 1.3 Derive-Secret function.
Derives a secret from a base secret and transcript hash.
**Parameters:**
- `out` - Output buffer (32 bytes for SHA-256, 48 for SHA-384)
- `secret` - The base secret
- `secret_len` - Secret length
- `label` - The label string (e.g., "c hs traffic")
- `label_len` - Label length
- `transcript_hash` - Hash of handshake messages (or empty hash for "")
- `hash_len` - Hash length (32 or 48)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_early_secret`
```c
void fio_tls13_derive_early_secret(void *restrict early_secret, const void *restrict psk, size_t psk_len, int use_sha384)
```
Derive the Early Secret from PSK.
Early Secret = HKDF-Extract(salt=0, IKM=PSK)
**Parameters:**
- `early_secret` - Output buffer (32 or 48 bytes)
- `psk` - Pre-shared key (or NULL/zeros for no PSK)
- `psk_len` - PSK length (0 if no PSK)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_handshake_secret`
```c
void fio_tls13_derive_handshake_secret(void *restrict handshake_secret, const void *restrict early_secret, const void *restrict ecdhe_secret, size_t ecdhe_len, int use_sha384)
```
Derive the Handshake Secret from ECDHE shared secret.
Handshake Secret = HKDF-Extract(
salt=Derive-Secret(Early Secret, "derived", ""),
IKM=ECDHE shared secret
)
**Parameters:**
- `handshake_secret` - Output buffer (32 or 48 bytes)
- `early_secret` - The early secret (from fio_tls13_derive_early_secret)
- `ecdhe_secret` - The ECDHE shared secret (e.g., from X25519)
- `ecdhe_len` - ECDHE secret length (32 for X25519)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_master_secret`
```c
void fio_tls13_derive_master_secret(void *restrict master_secret, const void *restrict handshake_secret, int use_sha384)
```
Derive the Master Secret.
Master Secret = HKDF-Extract(
salt=Derive-Secret(Handshake Secret, "derived", ""),
IKM=0
)
**Parameters:**
- `master_secret` - Output buffer (32 or 48 bytes)
- `handshake_secret` - The handshake secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_traffic_keys`
```c
void fio_tls13_derive_traffic_keys(void *restrict key, size_t key_len, void *restrict iv, const void *restrict traffic_secret, int use_sha384)
```
Derive traffic keys and IV from a traffic secret.
**Parameters:**
- `key` - Output buffer for write key
- `key_len` - Key length (16 for AES-128, 32 for AES-256/ChaCha20)
- `iv` - Output buffer for write IV (12 bytes)
- `traffic_secret` - The traffic secret (client/server handshake/app)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_finished_key`
```c
void fio_tls13_derive_finished_key(void *restrict finished_key, const void *restrict traffic_secret, int use_sha384)
```
Derive the Finished key from a traffic secret.
finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
**Parameters:**
- `finished_key` - Output buffer (32 or 48 bytes)
- `traffic_secret` - The handshake traffic secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_compute_finished`
```c
void fio_tls13_compute_finished(void *restrict verify_data, const void *restrict finished_key, const void *restrict transcript_hash, int use_sha384)
```
Compute the Finished verify_data.
verify_data = HMAC(finished_key, Transcript-Hash(Handshake Context))
**Parameters:**
- `verify_data` - Output buffer (32 or 48 bytes)
- `finished_key` - The finished key (from fio_tls13_derive_finished_key)
- `transcript_hash` - Hash of handshake messages up to this point
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_update_traffic_secret`
```c
void fio_tls13_update_traffic_secret(void *restrict new_secret, const void *restrict current_secret, int use_sha384)
```
Update application traffic secret for key update.
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N, "traffic upd", "",
Hash.length)
**Parameters:**
- `new_secret` - Output buffer (32 or 48 bytes)
- `current_secret` - Current application traffic secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_build_nonce`
```c
inline void fio_tls13_build_nonce(uint8_t nonce[12], const uint8_t iv[12], uint64_t seq)
```
Build per-record nonce by XORing sequence number with IV.
Per RFC 8446 Section 5.3:
- Pad 64-bit sequence number to 12 bytes (big-endian, left-padded with zeros)
- XOR with the static IV derived from traffic secret
**Parameters:**
- `nonce` - Output buffer (must be 12 bytes)
- `iv` - Static IV from key derivation (12 bytes)
- `seq` - 64-bit sequence number
_Symbol type:_ `function`
#### `fio_tls13_record_parse_header`
```c
const uint8_t *fio_tls13_record_parse_header( const uint8_t *data, size_t data_len, fio_tls13_content_type_e *content_type, size_t *payload_len)
```
Parse a TLS record header.
**Parameters:**
- `data` - Input buffer containing record data
- `data_len` - Length of input buffer
- `content_type` - Output: content type from header
- `payload_len` - Output: payload length from header
**Returns:**
- Pointer to payload data, or NULL if incomplete/invalid
_Symbol type:_ `function`
#### `fio_tls13_record_encrypt`
```c
int fio_tls13_record_encrypt(uint8_t *out, size_t out_capacity, const uint8_t *plaintext, size_t plaintext_len, fio_tls13_content_type_e content_type, fio_tls13_record_keys_s *keys)
```
Encrypt a TLS 1.3 record.
Per RFC 8446 Section 5.2:
- Output format: 5-byte header + encrypted(plaintext + content_type) + tag
- AAD is the 5-byte record header
- Sequence number is incremented after encryption
**Parameters:**
- `out` - Output buffer for encrypted record
- `out_capacity` - Capacity of output buffer
- `plaintext` - Plaintext data to encrypt
- `plaintext_len` - Length of plaintext
- `content_type` - Content type (appended to plaintext before encryption)
- `keys` - Encryption keys (sequence number will be incremented)
**Returns:**
- Total output length (header + ciphertext + tag), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_record_decrypt`
```c
int fio_tls13_record_decrypt(uint8_t *out, size_t out_capacity, fio_tls13_content_type_e *content_type, const uint8_t *ciphertext, size_t ciphertext_len, fio_tls13_record_keys_s *keys)
```
Decrypt a TLS 1.3 record.
Per RFC 8446 Section 5.2:
- Input includes 5-byte header
- Decrypts and verifies AEAD tag
- Scans backwards to find real content type (removes padding)
- Sequence number is incremented after successful decryption
Note: decryption is performed in-place in the ciphertext buffer, so the
ciphertext buffer will be modified. The output buffer only needs to hold
the plaintext (not the internal content type byte).
**Parameters:**
- `out` - Output buffer for decrypted plaintext
- `out_capacity` - Capacity of output buffer (>= plaintext length)
- `content_type` - Output: actual content type from inner plaintext
- `ciphertext` - Input ciphertext (includes 5-byte header, modified)
- `ciphertext_len` - Total length including header
- `keys` - Decryption keys (sequence number will be incremented)
**Returns:**
- Plaintext length (excluding padding and content type), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_record_keys_init`
```c
void fio_tls13_record_keys_init(fio_tls13_record_keys_s *keys, const uint8_t *key, uint8_t key_len, const uint8_t iv[12], fio_tls13_cipher_type_e cipher_type)
```
Initialize record keys structure.
**Parameters:**
- `keys` - Keys structure to initialize
- `key` - Key material (16 or 32 bytes)
- `key_len` - Key length (16 for AES-128, 32 for AES-256/ChaCha20)
- `iv` - IV material (12 bytes)
- `cipher_type` - Cipher type (fio_tls13_cipher_type_e)
_Symbol type:_ `function`
#### `fio_tls13_record_keys_clear`
```c
inline void fio_tls13_record_keys_clear(fio_tls13_record_keys_s *keys)
```
Clear sensitive key material from memory.
**Parameters:**
- `keys` - Keys structure to clear
_Symbol type:_ `function`
#### `fio_tls13_parse_handshake_header`
```c
const uint8_t *fio_tls13_parse_handshake_header( const uint8_t *data, size_t data_len, fio_tls13_handshake_type_e *msg_type, size_t *body_len)
```
Parse handshake header, return message type and body pointer.
Returns pointer to message body, or NULL on error.
Sets msg_type and body_len on success.
_Symbol type:_ `function`
#### `fio_tls13_write_handshake_header`
```c
void fio_tls13_write_handshake_header(uint8_t *out, fio_tls13_handshake_type_e msg_type, size_t body_len)
```
Write handshake header (4 bytes).
Format: HandshakeType (1 byte) + uint24 length (3 bytes)
_Symbol type:_ `function`
#### `fio_tls13_build_client_hello`
```c
int fio_tls13_build_client_hello(uint8_t *out, size_t out_capacity, const uint8_t random[32], const char *server_name, const uint8_t *x25519_pubkey, const uint16_t *cipher_suites, size_t cipher_suite_count)
```
Build a ClientHello message.
Returns: message length on success, -1 on error.
Parameters:
- out: output buffer
- out_capacity: size of output buffer
- random: 32-byte client random
- server_name: SNI hostname (NULL if not used)
- x25519_pubkey: 32-byte X25519 public key
- cipher_suites: array of cipher suites to offer
- cipher_suite_count: number of cipher suites
_Symbol type:_ `function`
#### `fio_tls13_parse_server_hello`
```c
int fio_tls13_parse_server_hello(fio_tls13_server_hello_s *out, const uint8_t *data, size_t data_len)
```
Parse ServerHello message.
Returns: 0 on success, -1 on error.
Note: data should point to the handshake body (after the 4-byte header).
_Symbol type:_ `function`
#### `fio_tls13_parse_encrypted_extensions`
```c
int fio_tls13_parse_encrypted_extensions( fio_tls13_encrypted_extensions_s *out, const uint8_t *data, size_t data_len)
```
Parse EncryptedExtensions message.
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_certificate`
```c
int fio_tls13_parse_certificate(fio_tls13_certificate_s *out, const uint8_t *data, size_t data_len)
```
Parse Certificate message (extracts first certificate only).
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_certificate_verify`
```c
int fio_tls13_parse_certificate_verify( fio_tls13_certificate_verify_s *out, const uint8_t *data, size_t data_len)
```
Parse CertificateVerify message.
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_build_finished`
```c
int fio_tls13_build_finished(uint8_t *out, size_t out_capacity, const uint8_t *verify_data, size_t verify_data_len)
```
Build Finished message.
verify_data = HMAC(finished_key, Transcript-Hash(Handshake Context))
Returns: message length on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_finished`
```c
int fio_tls13_parse_finished(const uint8_t *data, size_t data_len, const uint8_t *expected_verify_data, size_t verify_data_len)
```
Parse and verify Finished message.
Returns: 0 on success (MAC matches), -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_build_alert`
```c
inline int fio_tls13_build_alert(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc)
```
Build an alert message (unencrypted, for use before handshake keys).
Alert format: [level:1][description:1]
**Parameters:**
- `out` - Output buffer for alert message (2 bytes minimum)
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
**Returns:**
- Alert message length (2), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_send_alert`
```c
int fio_tls13_send_alert(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc, fio_tls13_record_keys_s *keys)
```
Build an encrypted alert record.
Per RFC 8446 Section 6, alerts are encrypted after handshake keys are
established. In TLS 1.3, all alerts except close_notify are effectively
fatal and the connection must be closed after sending.
**Parameters:**
- `out` - Output buffer for encrypted alert record
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
- `keys` - Encryption keys (sequence number will be incremented)
**Returns:**
- Encrypted record length, or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_send_alert_plaintext`
```c
int fio_tls13_send_alert_plaintext(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc)
```
Build an unencrypted alert record (for use before encryption is enabled).
**Parameters:**
- `out` - Output buffer for alert record
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
**Returns:**
- Record length (7 bytes: 5 header + 2 alert), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_alert_name`
```c
inline const char *fio_tls13_alert_name(uint8_t alert_desc)
```
Get human-readable name for an alert description.
**Parameters:**
- `alert_desc` - Alert description code
**Returns:**
- Static string with alert name
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/201 string.h`
47 public symbols.
### Macros
#### `FIO_STR_OPTIMIZE_EMBEDDED`
```c
#define FIO_STR_OPTIMIZE_EMBEDDED 0
```
For each unit (0 by default), adds `sizeof(char *)` bytes to the type size,
increasing the amount of strings that could be embedded within the type
without additional memory allocation.
For example, when using a reference counter wrapper on a 64bit system, it
would make sense to set this value to 1 - allowing the type size to fully
utilize a 16 byte memory allocation alignment.
_Symbol type:_ `macro`
#### `FIO_STR_OPTIMIZE4IMMUTABILITY`
```c
#define FIO_STR_OPTIMIZE4IMMUTABILITY 0
```
Minimizes the struct size, storing only string length and pointer.
By avoiding extra (mutable related) data, such as the allocated memory's
capacity, strings require less memory. However, this does introduce a
performance penalty when editing the string data.
_Symbol type:_ `macro`
#### `FIO_STR_INIT`
```c
#define FIO_STR_INIT \
{ .special = 0 }
```
This value should be used for initialization. For example:
// on the stack
fio_str_s str = FIO_STR_INIT;
// or on the heap
fio_str_s *str = malloc(sizeof(*str));
*str = FIO_STR_INIT;
Remember to cleanup:
// on the stack
fio_str_destroy(&str);
// or on the heap
fio_str_free(str);
free(str);
_Symbol type:_ `macro`
#### `FIO_STR_INIT_EXISTING`
```c
#define FIO_STR_INIT_EXISTING(buffer, length, capacity) \
{ .capa = (capacity), .len = (length), .buf = (buffer) }
```
This macro allows the container to be initialized with existing data, as long
as it's memory was allocated with the same allocator (`malloc` /
`fio_malloc`).
The `capacity` value should exclude the NUL character (if exists).
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_INIT_STATIC`
```c
#define FIO_STR_INIT_STATIC(buffer) \
{ \
.special = 4, .capa = FIO_STRLEN((buffer)), .len = FIO_STRLEN((buffer)), \
.buf = (char *)(buffer) \
}
```
This macro allows the container to be initialized with existing static data,
that shouldn't be freed.
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_INIT_STATIC2`
```c
#define FIO_STR_INIT_STATIC2(buffer, length) \
{ .special = 4, .capa = (length), .len = (length), .buf = (char *)(buffer) }
```
This macro allows the container to be initialized with existing static data,
that shouldn't be freed.
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_WRITE2`
```c
#define FIO_STR_WRITE2(str_name, dest, ...) \
FIO_NAME(str_name, __write2)(dest, (fio_string_write_s[]){__VA_ARGS__, {0}})
```
_Symbol type:_ `macro`
### Types
#### `fiobj_str_s`
```c
typedef struct {
/* String flags:
*
* bit 1: small string.
* bit 2: frozen string.
* bit 3: static (non allocated) string (big strings only).
* bit 3-8: small string length (up to 64 bytes).
*/
uint8_t special;
uint8_t reserved[(sizeof(void *) * (1 + FIO_STR_OPTIMIZE_EMBEDDED)) -
(sizeof(uint8_t))]; /* padding length */
#if !FIO_STR_OPTIMIZE4IMMUTABILITY
size_t capa; /* known capacity for longer Strings */
size_t len; /* String length for longer Strings */
#endif /* FIO_STR_OPTIMIZE4IMMUTABILITY */
char *buf; /* pointer for longer Strings */
} fiobj_str_s
```
The `fio_str_s` type should be considered opaque.
The type's attributes should be accessed ONLY through the accessor
functions: `fio_str2cstr`, `fio_str_len`, `fio_str2ptr`, `fio_str_capa`,
etc'.
Note: when the `small` flag is present, the structure is ignored and used
as raw memory for a small String (no additional allocation). This changes
the String's behavior drastically and requires that the accessor functions
be used.
_Symbol type:_ `type`
### Functions
#### `fiobj_str_init_const`
```c
inline fio_str_info_s fiobj_str_init_const(FIO_STR_PTR s, const char *str, size_t len)
```
Initializes the container with the provided static / constant string.
The string will be copied to the container **only** if it will fit in the
container itself. Otherwise, the supplied pointer will be used as is and it
should remain valid until the string is destroyed.
The final string can be safely be destroyed (using the `destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_init_copy`
```c
inline fio_str_info_s fiobj_str_init_copy(FIO_STR_PTR s, const char *str, size_t len)
```
Initializes the container with a copy of the provided dynamic string.
The string is always copied and the final string must be destroyed (using the
`destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_init_copy2`
```c
inline fio_str_info_s fiobj_str_init_copy2(FIO_STR_PTR dest, FIO_STR_PTR src)
```
Initializes the container with a copy of an existing String object.
The string is always copied and the final string must be destroyed (using the
`destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_destroy`
```c
inline void fiobj_str_destroy(FIO_STR_PTR s)
```
Frees the String's resources and re-initializes the container.
Note: if the container isn't allocated on the stack, it should be freed
separately using the appropriate `free` function.
_Symbol type:_ `function`
#### `fiobj_str_detach`
```c
inline char *fiobj_str_detach(FIO_STR_PTR s)
```
Returns a C string with the existing data, re-initializing the String.
Note: the String data is removed from the container, but the container
isn't freed.
Returns NULL if there's no String data.
NOTE: Returned string is ALWAYS dynamically allocated. Remember to free.
_Symbol type:_ `function`
#### `fiobj_str_dealloc`
```c
inline void fiobj_str_dealloc(void *ptr)
```
Frees the pointer returned by `detach`.
_Symbol type:_ `function`
#### `fiobj_str_info`
```c
inline fio_str_info_s fiobj_str_info(const FIO_STR_PTR s)
```
Returns the String's complete state (capacity, length and pointer).
_Symbol type:_ `function`
#### `fiobj_str_buf`
```c
inline fio_buf_info_s fiobj_str_buf(const FIO_STR_PTR s)
```
Returns the String's partial state (length and pointer).
_Symbol type:_ `function`
#### `fiobj_str_ptr`
```c
inline char *fiobj_str_ptr(FIO_STR_PTR s)
```
Returns a pointer (`char *`) to the String's content.
_Symbol type:_ `function`
#### `fiobj_str_len`
```c
inline size_t fiobj_str_len(FIO_STR_PTR s)
```
Returns the String's length in bytes.
_Symbol type:_ `function`
#### `fiobj_str_capa`
```c
inline size_t fiobj_str_capa(FIO_STR_PTR s)
```
Returns the String's existing capacity (total used & available memory).
_Symbol type:_ `function`
#### `fiobj_str_freeze`
```c
inline void fiobj_str_freeze(FIO_STR_PTR s)
```
Prevents further manipulations to the String's content.
_Symbol type:_ `function`
#### `fiobj_str_is_frozen`
```c
inline uint8_t fiobj_str_is_frozen(FIO_STR_PTR s)
```
Returns true if the string is frozen.
_Symbol type:_ `function`
#### `fiobj_str_is_allocated`
```c
inline int fiobj_str_is_allocated(const FIO_STR_PTR s)
```
Returns 1 if memory was allocated (and the String must be destroyed).
_Symbol type:_ `function`
#### `fiobj_str_is_eq`
```c
inline int fiobj_str_is_eq(const FIO_STR_PTR str1, const FIO_STR_PTR str2)
```
Binary comparison returns `1` if both strings are equal and `0` if not.
_Symbol type:_ `function`
#### `fiobj_str_hash`
```c
inline uint64_t fiobj_str_hash(const FIO_STR_PTR s, uint64_t seed)
```
Returns the string's Risky Hash value.
Note: Hash algorithm might change without notice.
_Symbol type:_ `function`
#### `fiobj_str_resize`
```c
inline fio_str_info_s fiobj_str_resize(FIO_STR_PTR s, size_t size)
```
Sets the new String size without reallocating any memory (limited by
existing capacity).
Returns the updated state of the String.
Note: When shrinking, any existing data beyond the new size may be
corrupted.
_Symbol type:_ `function`
#### `fiobj_str_compact`
```c
inline void fiobj_str_compact(FIO_STR_PTR s)
```
Performs a best attempt at minimizing memory consumption.
Actual effects depend on the underlying memory allocator and it's
implementation. Not all allocators will free any memory.
_Symbol type:_ `function`
#### `fiobj_str_reserve`
```c
fio_str_info_s fiobj_str_reserve(FIO_STR_PTR s, size_t amount)
```
Reserves (at least) `amount` of bytes for the string's data.
`amount` is in addition to existing String length.
Make sure to call `resize` with the updated information once the editing is
done.
Returns the updated state of the String.
_Symbol type:_ `function`
#### `fiobj_str_utf8_valid`
```c
size_t fiobj_str_utf8_valid(FIO_STR_PTR s)
```
Returns 1 if the String is UTF-8 valid and 0 if not.
_Symbol type:_ `function`
#### `fiobj_str_utf8_len`
```c
size_t fiobj_str_utf8_len(FIO_STR_PTR s)
```
Returns the String's length in UTF-8 characters.
_Symbol type:_ `function`
#### `fiobj_str_utf8_select`
```c
int fiobj_str_utf8_select(FIO_STR_PTR s, intptr_t *pos, size_t *len)
```
Takes a UTF-8 character selection information (UTF-8 position and length)
and updates the same variables so they reference the raw byte slice
information.
If the String isn't UTF-8 valid up to the requested selection, than `pos`
will be updated to `-1` otherwise values are always positive.
The returned `len` value may be shorter than the original if there wasn't
enough data left to accommodate the requested length. When a `len` value of
`0` is returned, this means that `pos` marks the end of the String.
Returns -1 on error and 0 on success.
_Symbol type:_ `function`
#### `fiobj_str_write`
```c
inline fio_str_info_s fiobj_str_write(FIO_STR_PTR s, const void *src, size_t src_len)
```
Writes data at the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_concat`
```c
fio_str_info_s fiobj_str_concat(FIO_STR_PTR dest, FIO_STR_PTR const src)
```
Appends the `src` String to the end of the `dest` String.
If `dest` is empty, the resulting Strings will be equal.
_Symbol type:_ `function`
#### `fiobj_str_join`
```c
inline fio_str_info_s fiobj_str_join(FIO_STR_PTR dest, FIO_STR_PTR const src)
```
Alias for fio_str_concat
_Symbol type:_ `function`
#### `fiobj_str_replace`
```c
fio_str_info_s fiobj_str_replace(FIO_STR_PTR s, intptr_t start_pos, size_t old_len, const void *src, size_t src_len)
```
Replaces the data in the String - replacing `old_len` bytes starting at
`start_pos`, with the data at `src` (`src_len` bytes long).
Negative `start_pos` values are calculated backwards, `-1` == end of
String.
When `old_len` is zero, the function will insert the data at `start_pos`.
If `src_len == 0` than `src` will be ignored and the data marked for
replacement will be erased.
_Symbol type:_ `function`
#### `fiobj_str_write_i`
```c
fio_str_info_s fiobj_str_write_i(FIO_STR_PTR s, int64_t num)
```
Writes a number at the end of the String using normal base 10 notation.
_Symbol type:_ `function`
#### `fiobj_str_write_hex`
```c
fio_str_info_s fiobj_str_write_hex(FIO_STR_PTR s, int64_t num)
```
Writes a number at the end of the String using Hex (base 16) notation.
_Symbol type:_ `function`
#### `fiobj_str_write_bin`
```c
fio_str_info_s fiobj_str_write_bin(FIO_STR_PTR s, int64_t num)
```
_Symbol type:_ `function`
#### `fiobj_str_vprintf`
```c
fio_str_info_s fiobj_str_vprintf(FIO_STR_PTR s, const char *format, va_list argv)
```
Writes to the String using a vprintf like interface.
Data is written to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_printf`
```c
fio_str_info_s fiobj_str_printf(FIO_STR_PTR s, const char *format, ...)
```
Writes to the String using a printf like interface.
Data is written to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_write_escape`
```c
fio_str_info_s fiobj_str_write_escape(FIO_STR_PTR s, const void *data, size_t data_len)
```
Writes data at the end of the String, escaping the data using JSON semantics.
The JSON semantic are common to many programming languages, promising a UTF-8
String while making it easy to read and copy the string during debugging.
_Symbol type:_ `function`
#### `fiobj_str_write_unescape`
```c
fio_str_info_s fiobj_str_write_unescape(FIO_STR_PTR s, const void *escaped, size_t len)
```
Writes an escaped data into the string after unescaping the data.
_Symbol type:_ `function`
#### `fiobj_str_write_base64enc`
```c
fio_str_info_s fiobj_str_write_base64enc(FIO_STR_PTR s, const void *data, size_t data_len, uint8_t url_encoded)
```
Writes data at the end of the String, encoding the data as Base64 encoded
data.
_Symbol type:_ `function`
#### `fiobj_str_write_base64dec`
```c
fio_str_info_s fiobj_str_write_base64dec(FIO_STR_PTR s, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_write_html_escape`
```c
fio_str_info_s fiobj_str_write_html_escape(FIO_STR_PTR s, const void *raw, size_t len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fiobj_str_write_html_unescape`
```c
fio_str_info_s fiobj_str_write_html_unescape(FIO_STR_PTR s, const void *escaped, size_t len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fiobj_str_readfd`
```c
fio_str_info_s fiobj_str_readfd(FIO_STR_PTR s, int fd, intptr_t start_at, intptr_t limit)
```
Reads data from a file descriptor `fd` at offset `start_at` and pastes it's
contents (or a slice of it) at the end of the String. If `limit == 0`, than
the data will be read until EOF.
The file should be a regular file or the operation might fail (can't be used
for sockets).
The file descriptor will remain open and should be closed manually.
_Symbol type:_ `function`
#### `fiobj_str_readfile`
```c
fio_str_info_s fiobj_str_readfile(FIO_STR_PTR s, const char *filename, intptr_t start_at, intptr_t limit)
```
Opens the file `filename` and pastes it's contents (or a slice ot it) at
the end of the String. If `limit == 0`, than the data will be read until
EOF.
If the file can't be located, opened or read, or if `start_at` is beyond
the EOF position, NULL is returned in the state's `data` field.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/202 array.h`
30 public symbols.
### Macros
#### `FIO_ARRAY_NOT_FOUND`
```c
#define FIO_ARRAY_NOT_FOUND ((uint32_t)-1)
```
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE`
```c
#define FIO_ARRAY_TYPE void *
```
The type for array elements (an array of FIO_ARRAY_TYPE)
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_INVALID`
```c
#define FIO_ARRAY_TYPE_INVALID NULL
```
An invalid value for that type (if any).
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_INVALID_SIMPLE`
```c
#define FIO_ARRAY_TYPE_INVALID_SIMPLE 0
```
Is the FIO_ARRAY_TYPE_INVALID object memory is all zero? (yes = 1)
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_COPY`
```c
#define FIO_ARRAY_TYPE_COPY(dest, src) (dest) = (src)
```
Handles a copy operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_DESTROY`
```c
#define FIO_ARRAY_TYPE_DESTROY(obj)
```
Handles a destroy / free operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_CMP`
```c
#define FIO_ARRAY_TYPE_CMP(a, b) (a) == (b)
```
Handles a comparison operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_INIT`
```c
#define FIO_ARRAY_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_ARRAY_EACH`
```c
#define FIO_ARRAY_EACH(array_name, array, pos) \
for (FIO_NAME(array_name, ____type_t) \
*first___ai = NULL, \
*pos = FIO_NAME(array_name, each_next)((array), &first___ai, NULL); \
pos; \
pos = FIO_NAME(array_name, each_next)((array), &first___ai, pos))
```
Iterates through the array using a `for` loop.
Access the object with the pointer `pos`. The `pos` variable can be named
however you please.
Avoid editing the array during a FOR loop, although I hope it's possible, I
wouldn't count on it.
**Note**: this variant supports automatic pointer tagging / untagging.
_Symbol type:_ `macro`
### Types
#### `fiobj_array_s`
```c
struct fiobj_array_s {
/* start common header (with embedded array type) */
/** the offset to the first item. */
uint32_t start;
/** The offset to the first empty location the array. */
uint32_t end;
/* end common header (with embedded array type) */
/** The array's capacity - limited to 32bits, but we use the extra padding. */
uint32_t capa;
uint32_t padding__;
/** a pointer to the array's memory (if not embedded) */
FIO_ARRAY_TYPE *ary;
#if FIO_ARRAY_ENABLE_EMBEDDED > 1
/** Do we wanted larger small-array optimizations? */
FIO_ARRAY_TYPE
extra_memory_for_embedded_arrays[(FIO_ARRAY_ENABLE_EMBEDDED - 1)]
#endif
}
```
an Array type.
_Symbol type:_ `type`
#### `fiobj_array_each_s`
```c
struct fiobj_array_each_s {
/** The array iterated. Once set, cannot be safely changed. */
FIO_ARRAY_PTR const parent;
/** The current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct FIO_NAME(FIO_ARRAY_NAME, each_s) * info);
/** Opaque user data. */
void *udata;
/** The object / value at the current index. */
FIO_ARRAY_TYPE value;
/* memory padding used for FIOBJ */
uint64_t padding;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_array_destroy`
```c
void fiobj_array_destroy(FIO_ARRAY_PTR ary)
```
_Symbol type:_ `function`
#### `fiobj_array_count`
```c
inline uint32_t fiobj_array_count(FIO_ARRAY_PTR ary)
```
Returns the number of elements in the Array.
_Symbol type:_ `function`
#### `fiobj_array_capa`
```c
inline uint32_t fiobj_array_capa(FIO_ARRAY_PTR ary)
```
Returns the current, temporary, array capacity (it's dynamic).
_Symbol type:_ `function`
#### `fiobj_array_is_embedded`
```c
inline int fiobj_array_is_embedded(FIO_ARRAY_PTR ary)
```
Returns 1 if the array is embedded, 0 if it has memory allocated and -1 on an
error.
_Symbol type:_ `function`
#### `fiobj_array2ptr`
```c
inline FIO_ARRAY_TYPE *fiobj_array2ptr(FIO_ARRAY_PTR ary)
```
Returns a pointer to the C array containing the objects.
_Symbol type:_ `function`
#### `fiobj_array_reserve`
```c
uint32_t fiobj_array_reserve(FIO_ARRAY_PTR ary, int64_t capa)
```
Reserves a minimal capacity for additional elements to be added to the array.
If `capa` is negative, new memory will be allocated at the beginning of the
array rather then it's end.
Returns the array's new capacity.
_Symbol type:_ `function`
#### `fiobj_array_concat`
```c
FIO_ARRAY_PTR fiobj_array_concat(FIO_ARRAY_PTR dest, FIO_ARRAY_PTR src)
```
Adds all the items in the `src` Array to the end of the `dest` Array.
The `src` Array remain untouched.
Always returns the destination array (`dest`).
_Symbol type:_ `function`
#### `fiobj_array_set`
```c
FIO_ARRAY_TYPE *fiobj_array_set(FIO_ARRAY_PTR ary, int64_t index, FIO_ARRAY_TYPE data, FIO_ARRAY_TYPE *old)
```
Sets `index` to the value in `data`.
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
If `old` isn't NULL, the existing data will be copied to the location pointed
to by `old` before the copy in the Array is destroyed.
Returns a pointer to the new object, or NULL on error.
_Symbol type:_ `function`
#### `fiobj_array_get`
```c
inline FIO_ARRAY_TYPE fiobj_array_get(FIO_ARRAY_PTR ary, int64_t index)
```
Returns the value located at `index` (no copying is performed).
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
_Symbol type:_ `function`
#### `fiobj_array_find`
```c
uint32_t fiobj_array_find(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data, int64_t start_at)
```
Returns the index of the object or (uint32_t)-1 if the object wasn't found.
If `start_at` is negative (i.e., -1), than seeking will be performed in
reverse, where -1 == last index (-2 == second to last, etc').
_Symbol type:_ `function`
#### `fiobj_array_remove`
```c
int fiobj_array_remove(FIO_ARRAY_PTR ary, int64_t index, FIO_ARRAY_TYPE *old)
```
Removes an object from the array, MOVING all the other objects to prevent
"holes" in the data.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns 0 on success and -1 on error.
This action is O(n) where n in the length of the array.
It could get expensive.
_Symbol type:_ `function`
#### `fiobj_array_remove2`
```c
uint32_t fiobj_array_remove2(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Removes all occurrences of an object from the array (if any), MOVING all the
existing objects to prevent "holes" in the data.
Returns the number of items removed.
This action is O(n) where n in the length of the array.
It could get expensive.
_Symbol type:_ `function`
#### `fiobj_array_compact`
```c
void fiobj_array_compact(FIO_ARRAY_PTR ary)
```
Attempts to lower the array's memory consumption.
_Symbol type:_ `function`
#### `fiobj_array_push`
```c
FIO_ARRAY_TYPE *fiobj_array_push(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Pushes an object to the end of the Array. Returns a pointer to the new object
or NULL on error.
_Symbol type:_ `function`
#### `fiobj_array_pop`
```c
int fiobj_array_pop(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE *old)
```
Removes an object from the end of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
_Symbol type:_ `function`
#### `fiobj_array_unshift`
```c
FIO_ARRAY_TYPE *fiobj_array_unshift(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Unshifts an object to the beginning of the Array. Returns a pointer to the
new object or NULL on error.
This could be expensive, causing `memmove`.
_Symbol type:_ `function`
#### `fiobj_array_shift`
```c
int fiobj_array_shift(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE *old)
```
Removes an object from the beginning of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
_Symbol type:_ `function`
#### `fiobj_array_each`
```c
uint32_t fiobj_array_each(FIO_ARRAY_PTR ary, int (*task)(FIO_NAME(FIO_ARRAY_NAME, each_s) * info), void *udata, int64_t start_at)
```
Iteration using a callback for each entry in the array.
The callback task function must accept an each_s pointer, see above.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
_Symbol type:_ `function`
#### `fiobj_array_each_next`
```c
inline FIO_ARRAY_TYPE *fiobj_array_each_next(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE **first, FIO_ARRAY_TYPE *pos)
```
Returns a pointer to the (next) object in the array.
Returns a pointer to the first object if `pos == NULL` and there are objects
in the array.
The first pointer is automatically set and it allows object insertions and
memory effecting functions to be called from within the loop.
If the object in `pos` (or an object before it) were removed, consider
passing `pos-1` to the function, to avoid skipping any elements while
looping.
Returns the next object if both `first` and `pos` are valid.
Returns NULL if `pos` was the last object or no object exist.
Returns the first object if either `first` or `pos` are invalid.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/210 map.h`
30 public symbols.
### Macros
#### `FIO_MAP_INIT`
```c
#define FIO_MAP_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_MAP_EACH`
```c
#define FIO_MAP_EACH(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_next)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_next)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
#### `FIO_MAP_EACH_REVERSED`
```c
#define FIO_MAP_EACH_REVERSED(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_prev)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_prev)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
### Types
#### `fiobj_hash_node_s`
```c
struct fiobj_hash_node_s
```
internal object data representation
_Symbol type:_ `type`
#### `fiobj_hash_s`
```c
struct fiobj_hash_s {
uint32_t bits;
uint32_t count;
FIO_NAME(FIO_MAP_NAME, node_s) * map;
#if FIO_MAP_ORDERED
FIO_INDEXED_LIST32_HEAD head;
#endif
}
```
A Hash Map / Set type
_Symbol type:_ `type`
#### `fiobj_hash_iterator_s`
```c
typedef struct {
/** the node in the internal map */
fiobj_hash_iterator_s * node;
/** the key in the current position */
FIO_MAP_KEY key;
#ifdef FIO_MAP_VALUE
/** the value in the current position */
FIO_MAP_VALUE value;
#endif
#if !FIO_MAP_RECALC_HASH
/** the hash for the current position */
uint64_t hash;
#endif
struct { /* internal usage, do not access */
uint32_t index; /* the index in the internal map */
uint32_t pos; /* the position in the ordering scheme */
uintptr_t map_validator; /* map mutation guard */
} private_;
} fiobj_hash_iterator_s
```
Map iterator type
_Symbol type:_ `type`
#### `fiobj_hash_each_s`
```c
struct fiobj_hash_each_s {
/** The being iterated. Once set, cannot be safely changed. */
FIO_MAP_PTR const parent;
/** The current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct FIO_NAME(FIO_MAP_NAME, each_s) * info);
/** Opaque user data. */
void *udata;
#ifdef FIO_MAP_VALUE
/** The object's value at the current index. */
FIO_MAP_VALUE value;
#endif
/** The object's key the current index. */
FIO_MAP_KEY key;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_hash_destroy`
```c
void fiobj_hash_destroy(FIO_MAP_PTR map)
```
Destroys the object, re-initializing its container.
_Symbol type:_ `function`
#### `fiobj_hash_capa`
```c
inline uint32_t fiobj_hash_capa(FIO_MAP_PTR map)
```
Theoretical map capacity.
_Symbol type:_ `function`
#### `fiobj_hash_count`
```c
inline uint32_t fiobj_hash_count(FIO_MAP_PTR map)
```
The number of objects in the map capacity.
_Symbol type:_ `function`
#### `fiobj_hash_reserve`
```c
void fiobj_hash_reserve(FIO_MAP_PTR map, size_t capa)
```
Reserves at minimum the capacity requested.
_Symbol type:_ `function`
#### `fiobj_hash_node2key`
```c
inline FIO_MAP_KEY fiobj_hash_node2key(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the key value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2hash`
```c
inline uint64_t fiobj_hash_node2hash(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the hash value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2val`
```c
inline FIO_MAP_VALUE fiobj_hash_node2val(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2key_ptr`
```c
inline FIO_MAP_KEY_INTERNAL *fiobj_hash_node2key_ptr( FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the key value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2val_ptr`
```c
inline FIO_MAP_VALUE_INTERNAL *fiobj_hash_node2val_ptr( FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_remove`
```c
int fiobj_hash_remove(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key, #if defined(FIO_MAP_VALUE) FIO_MAP_VALUE_INTERNAL *old #else FIO_MAP_KEY_INTERNAL *old #endif )
```
Removes an object in the map, returning a pointer to the map data.
_Symbol type:_ `function`
#### `fiobj_hash_evict`
```c
void fiobj_hash_evict(FIO_MAP_PTR map, size_t number_of_elements)
```
Evicts elements in order least recently used (LRU), FIFO or undefined.
_Symbol type:_ `function`
#### `fiobj_hash_clear`
```c
void fiobj_hash_clear(FIO_MAP_PTR map)
```
Removes all objects from the map, without releasing the map's resources.
_Symbol type:_ `function`
#### `fiobj_hash_compact`
```c
void fiobj_hash_compact(FIO_MAP_PTR map)
```
Attempts to minimize memory use.
_Symbol type:_ `function`
#### `fiobj_hash_get`
```c
inline FIO_MAP_GET_T fiobj_hash_get(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key)
```
Gets a value from the map, if exists.
_Symbol type:_ `function`
#### `fiobj_hash_set`
```c
inline FIO_MAP_GET_T fiobj_hash_set(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif #ifdef FIO_MAP_VALUE FIO_MAP_KEY key, FIO_MAP_VALUE obj, FIO_MAP_VALUE_INTERNAL *old #else FIO_MAP_KEY key #endif )
```
Sets a value in the map, hash maps will overwrite existing data if any.
_Symbol type:_ `function`
#### `fiobj_hash_set_if_missing`
```c
inline FIO_MAP_GET_T fiobj_hash_set_if_missing(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key #ifdef FIO_MAP_VALUE , FIO_MAP_VALUE obj #endif )
```
Sets a value in the map if not set previously.
_Symbol type:_ `function`
#### `fiobj_hash_set_ptr`
```c
fiobj_hash_set_ptr * FIO_NAME(FIO_MAP_NAME, set_ptr)(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif #ifdef FIO_MAP_VALUE FIO_MAP_KEY key, FIO_MAP_VALUE val, FIO_MAP_VALUE_INTERNAL *old, int overwrite #else FIO_MAP_KEY key #endif )
```
The core set function.
This function returns `NULL` on error (errors are logged).
If the map is a hash map, overwriting the value (while keeping the key) is
possible. In this case the `old` pointer is optional, and if set than the old
data will be copied to over during an overwrite.
NOTE: the function returns a pointer to the map's internal storage.
_Symbol type:_ `function`
#### `fiobj_hash_get_ptr`
```c
fiobj_hash_get_ptr * FIO_NAME(FIO_MAP_NAME, get_ptr)(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key)
```
The core get function. This function returns NULL if item is missing.
NOTE: the function returns a pointer to the map's internal storage.
_Symbol type:_ `function`
#### `fiobj_hash_each`
```c
uint32_t fiobj_hash_each(FIO_MAP_PTR map, int (*task)(FIO_NAME(FIO_MAP_NAME, each_s) *), void *udata, ssize_t start_at)
```
Iteration using a callback for each element in the map.
The callback task function must accept an each_s pointer, see above.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
_Symbol type:_ `function`
#### `fiobj_hash_get_next`
```c
fiobj_hash_get_next FIO_NAME(FIO_MAP_NAME, get_next)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * current_pos)
```
Returns the next iterator object after `current_pos` or the first if `NULL`.
Note that adding objects to the map or rehashing between iterations could
incur performance penalties when re-setting and re-seeking the previous
iterator position.
Adding objects to, or rehashing, an unordered maps could invalidate the
iterator object completely as the ordering may have changed and so the "next"
object might be any object in the map.
_Symbol type:_ `function`
#### `fiobj_hash_get_prev`
```c
fiobj_hash_get_prev FIO_NAME(FIO_MAP_NAME, get_prev)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * current_pos)
```
Returns the next iterator object after `current_pos` or the last if `NULL`.
See notes in `get_next`.
_Symbol type:_ `function`
#### `fiobj_hash_iterator_is_valid`
```c
inline int fiobj_hash_iterator_is_valid(FIO_NAME(FIO_MAP_NAME, iterator_s) * iterator)
```
Returns 1 if the iterator is out of bounds, otherwise returns 0.
_Symbol type:_ `function`
#### `fiobj_hash_iterator2node`
```c
inline fiobj_hash_iterator2node * FIO_NAME(FIO_MAP_NAME, iterator2node)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * iterator)
```
Returns a pointer to the node object in the internal map.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/210 map2.h`
3 public symbols.
### Macros
#### `FIO_MAP2_INIT`
```c
#define FIO_MAP2_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_MAP2_EACH`
```c
#define FIO_MAP2_EACH(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_next)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_next)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
#### `FIO_MAP2_EACH_REVERSED`
```c
#define FIO_MAP2_EACH_REVERSED(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_prev)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_prev)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/249 reference counter.h`
17 public symbols.
### Functions
#### `fiobj_array_new`
```c
FIO_REF_TYPE_PTR fiobj_array_new(void)
```
_Symbol type:_ `function`
#### `fiobj_hash_new`
```c
FIO_REF_TYPE_PTR fiobj_hash_new(void)
```
_Symbol type:_ `function`
#### `fiobj_object_new`
```c
FIO_REF_TYPE_PTR fiobj_object_new(void)
```
_Symbol type:_ `function`
#### `fiobj_str_new`
```c
FIO_REF_TYPE_PTR fiobj_str_new(void)
```
_Symbol type:_ `function`
#### `fiobj_array_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_array_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_hash_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_hash_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_object_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_object_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_str_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_str_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_array_free`
```c
void fiobj_array_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_hash_free`
```c
void fiobj_hash_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_object_free`
```c
void fiobj_object_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_str_free`
```c
void fiobj_str_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_object_metadata`
```c
FIO_REF_METADATA *fiobj_object_metadata(FIO_REF_TYPE_PTR wrapped)
```
Returns a pointer to the object's metadata, if defined.
_Symbol type:_ `function`
#### `fiobj_array_references`
```c
inline size_t fiobj_array_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_hash_references`
```c
inline size_t fiobj_hash_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_object_references`
```c
inline size_t fiobj_object_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_str_references`
```c
inline size_t fiobj_str_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/250 fiobj.h`
66 public symbols.
### Macros
#### `FIOBJ_MAX_NESTING`
```c
#define FIOBJ_MAX_NESTING 512
```
Sets the limit on nesting level transversal by recursive functions.
This effects JSON output / input and the `fiobj_each2` function since they
are recursive.
HOWEVER: this value will NOT effect the recursive `fiobj_free` which could
(potentially) expload the stack if given melformed input such as cyclic data
structures.
Values should be less than 32K.
_Symbol type:_ `macro`
#### `FIOBJ_JSON_APPEND`
```c
#define FIOBJ_JSON_APPEND 1
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ALLOC`
```c
#define FIOBJ_MARK_MEMORY_ALLOC() \
fio_atomic_add(&FIOBJ_MARK_MEMORY_ALLOC_COUNTER, 1)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_FREE`
```c
#define FIOBJ_MARK_MEMORY_FREE() \
fio_atomic_add(&FIOBJ_MARK_MEMORY_FREE_COUNTER, 1)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_PRINT`
```c
#define FIOBJ_MARK_MEMORY_PRINT() \
FIO___LOG_PRINT_LEVEL( \
((FIOBJ_MARK_MEMORY_ALLOC_COUNTER == FIOBJ_MARK_MEMORY_FREE_COUNTER) \
? 4 /* FIO_LOG_LEVEL_INFO */ \
: 3 /* FIO_LOG_LEVEL_WARNING */), \
((FIOBJ_MARK_MEMORY_ALLOC_COUNTER == FIOBJ_MARK_MEMORY_FREE_COUNTER) \
? "INFO: total remaining FIOBJ allocations: %zu (%zu - %zu)" \
: "WARNING: LEAKED! FIOBJ allocations: %zu (%zu - %zu)"), \
FIOBJ_MARK_MEMORY_ALLOC_COUNTER - FIOBJ_MARK_MEMORY_FREE_COUNTER, \
FIOBJ_MARK_MEMORY_ALLOC_COUNTER, \
FIOBJ_MARK_MEMORY_FREE_COUNTER)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ENABLED`
```c
#define FIOBJ_MARK_MEMORY_ENABLED 1
```
If true, FIOBJ memory allocation counting is enabled.
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ALLOC_COUNTER`
```c
#define FIOBJ_MARK_MEMORY_ALLOC_COUNTER 0 /* when testing unmarked FIOBJ */
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_FREE_COUNTER`
```c
#define FIOBJ_MARK_MEMORY_FREE_COUNTER 0 /* when testing unmarked FIOBJ */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_NULL`
```c
#define FIOBJ_T_NULL 2 /* 0b010 a lonely second bit signifies a primitive */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_TRUE`
```c
#define FIOBJ_T_TRUE 18 /* 0b010 010 - primitive value */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_FALSE`
```c
#define FIOBJ_T_FALSE 34 /* 0b100 010 - primitive value */
```
_Symbol type:_ `macro`
#### `FIOBJ_TYPE`
```c
#define FIOBJ_TYPE(o) fiobj_type(o)
```
Use the macros to avoid future API changes.
_Symbol type:_ `macro`
#### `FIOBJ_TYPE_IS`
```c
#define FIOBJ_TYPE_IS(o, type) (fiobj_type(o) == type)
```
Use the macros to avoid future API changes.
_Symbol type:_ `macro`
#### `FIOBJ_T_INVALID`
```c
#define FIOBJ_T_INVALID 0
```
Identifies an invalid type identifier (returned from FIOBJ_TYPE(o)
_Symbol type:_ `macro`
#### `FIOBJ_INVALID`
```c
#define FIOBJ_INVALID 0
```
Identifies an invalid object
_Symbol type:_ `macro`
#### `FIOBJ_IS_INVALID`
```c
#define FIOBJ_IS_INVALID(o) (((uintptr_t)(o)&7UL) == 0)
```
Tests if the object is (probably) a valid FIOBJ
_Symbol type:_ `macro`
#### `FIOBJ_IS_NULL`
```c
#define FIOBJ_IS_NULL(o) (FIOBJ_IS_INVALID(o) || ((o) == FIOBJ_T_NULL))
```
_Symbol type:_ `macro`
#### `FIOBJ_TYPE_CLASS`
```c
#define FIOBJ_TYPE_CLASS(o) ((fiobj_class_en)(((uintptr_t)(o)) & 7UL))
```
_Symbol type:_ `macro`
#### `FIOBJ_PTR_TAG`
```c
#define FIOBJ_PTR_TAG(o, klass) ((uintptr_t)(((uintptr_t)(o)) | (klass)))
```
_Symbol type:_ `macro`
#### `FIOBJ_PTR_UNTAG`
```c
#define FIOBJ_PTR_UNTAG(o) ((uintptr_t)(((uintptr_t)(o)) & (~7ULL)))
```
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR`
```c
#define FIOBJ_STR_TEMP_VAR(str_name) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, __auto_mem_tmp) = {0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR_STATIC`
```c
#define FIOBJ_STR_TEMP_VAR_STATIC(str_name, buf_, len_) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, \
__auto_mem_tmp) = {0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT_STATIC2((buf_), (len_))}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack, initialized with a
static string.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR_EXISTING`
```c
#define FIOBJ_STR_TEMP_VAR_EXISTING(str_name, buf_, len_, capa_) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, __auto_mem_tmp) = { \
0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT_EXISTING((buf_), (len_), (capa_))}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack, initialized with a
static string.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_DESTROY`
```c
#define FIOBJ_STR_TEMP_DESTROY(str_name) \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), destroy)(str_name);
```
Resets a temporary FIOBJ String, freeing and any resources allocated.
_Symbol type:_ `macro`
### Types
#### `fiobj_class_en`
```c
typedef enum {
FIOBJ_T_NUMBER = 0x01, /* 0b001 3 bits taken for small numbers */
FIOBJ_T_PRIMITIVE = 2, /* 0b010 a lonely second bit signifies a primitive */
FIOBJ_T_STRING = 3, /* 0b011 */
FIOBJ_T_ARRAY = 4, /* 0b100 */
FIOBJ_T_HASH = 5, /* 0b101 */
FIOBJ_T_FLOAT = 6, /* 0b110 */
FIOBJ_T_OTHER = 7, /* 0b111 dynamic type - test content */
} fiobj_class_en
```
FIOBJ type enum for common / primitive types.
_Symbol type:_ `type`
#### `fiobj_each_s`
```c
struct fiobj_each_s {
/** The being iterated. Once set, cannot be safely changed. */
FIOBJ const parent;
/** The index to start at / the current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct fiobj_each_s *info);
/** The argument passed along to the task. */
void *udata;
/** The value of the current object in the Array or Hash Map */
FIOBJ value;
/* The key, if a Hash Map */
FIOBJ key;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_type`
```c
inline size_t fiobj_type(FIOBJ o)
```
Returns an objects type. This isn't limited to known types.
_Symbol type:_ `function`
#### `fiobj_dup`
```c
inline FIOBJ fiobj_dup(FIOBJ o)
```
Increases an object's reference count (or copies) and returns it.
_Symbol type:_ `function`
#### `fiobj_free`
```c
inline void fiobj_free(FIOBJ o)
```
Decreases an object's reference count or frees it.
_Symbol type:_ `function`
#### `fiobj_is_eq`
```c
inline unsigned char fiobj_is_eq(FIOBJ a, FIOBJ b)
```
Compares two objects.
_Symbol type:_ `function`
#### `fiobj2cstr`
```c
inline fio_str_info_s fiobj2cstr(FIOBJ o)
```
Returns a temporary String representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2i`
```c
inline intptr_t fiobj2i(FIOBJ o)
```
Returns an integer representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2f`
```c
inline double fiobj2f(FIOBJ o)
```
Returns a float (double) representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2hash`
```c
inline uint64_t fiobj2hash(FIOBJ object_key)
```
Calculates an object's hash value for a specific hash map object.
_Symbol type:_ `function`
#### `fiobj_each1`
```c
FIO_SFUNC uint32_t fiobj_each1(FIOBJ o, int (*task)(fiobj_each_s *info), void *udata, int32_t start_at)
```
Performs a task for each element held by the FIOBJ object.
If `task` returns -1, the `each` loop will break (stop).
Returns the "stop" position - the number of elements processed + `start_at`.
_Symbol type:_ `function`
#### `fiobj_each2`
```c
uint32_t fiobj_each2(FIOBJ o, int (*task)(fiobj_each_s *info), void *udata)
```
Performs a task for the object itself and each element held by the FIOBJ
object or any of it's elements (a deep task).
The order of performance is by order of appearance, as if all nesting levels
were flattened.
If `task` returns -1, the `each` loop will break (stop).
Returns the number of elements processed.
_Symbol type:_ `function`
#### `fiobj_true`
```c
inline FIOBJ fiobj_true(void)
```
Returns the `true` primitive.
_Symbol type:_ `function`
#### `fiobj_false`
```c
inline FIOBJ fiobj_false(void)
```
Returns the `false` primitive.
_Symbol type:_ `function`
#### `fiobj_null`
```c
inline FIOBJ fiobj_null(void)
```
Returns the `nil` / `null` primitive.
_Symbol type:_ `function`
#### `fiobj_num_new`
```c
inline FIOBJ FIO_NAME(fiobj_num_new, new)(intptr_t i)
```
Creates a new Number object.
_Symbol type:_ `function`
#### `fiobj_num2i`
```c
inline intptr_t FIO_NAME2(fiobj_num2i, i)(FIOBJ i)
```
Reads the number from a FIOBJ Number.
_Symbol type:_ `function`
#### `fiobj_num2f`
```c
inline double FIO_NAME2(fiobj_num2f, f)(FIOBJ i)
```
Reads the number from a FIOBJ Number, fitting it in a double.
_Symbol type:_ `function`
#### `fiobj_num2cstr`
```c
fio_str_info_s FIO_NAME2(fiobj_num2cstr, cstr)(FIOBJ i)
```
Returns a String representation of the number (in base 10).
_Symbol type:_ `function`
#### `fiobj_num_free`
```c
inline void FIO_NAME(fiobj_num_free, free)(FIOBJ i)
```
Frees a FIOBJ number.
_Symbol type:_ `function`
#### `fiobj_float_new`
```c
inline FIOBJ FIO_NAME(fiobj_float_new, new)(double i)
```
Creates a new Float (double) object.
_Symbol type:_ `function`
#### `fiobj_float2i`
```c
inline intptr_t FIO_NAME2(fiobj_float2i, i)(FIOBJ i)
```
Reads the number from a FIOBJ Float rounding it to an integer.
_Symbol type:_ `function`
#### `fiobj_float2f`
```c
inline double FIO_NAME2(fiobj_float2f, f)(FIOBJ i)
```
Reads the value from a FIOBJ Float, as a double.
_Symbol type:_ `function`
#### `fiobj_float2cstr`
```c
fio_str_info_s FIO_NAME2(fiobj_float2cstr, cstr)(FIOBJ i)
```
Returns a String representation of the float.
_Symbol type:_ `function`
#### `fiobj_float_free`
```c
inline void FIO_NAME(fiobj_float_free, free)(FIOBJ i)
```
Frees a FIOBJ Float.
_Symbol type:_ `function`
#### `fiobj_str_new_cstr`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_cstr, new_cstr)(const char *ptr, size_t len)
```
_Symbol type:_ `function`
#### `fiobj_str_new_buf`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_buf, new_buf)(size_t capa)
```
_Symbol type:_ `function`
#### `fiobj_str_new_copy`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_copy, new_copy)(FIOBJ original)
```
_Symbol type:_ `function`
#### `fiobj_str2cstr`
```c
inline fio_str_info_s FIO_NAME2(fiobj_str2cstr, cstr)(FIOBJ s)
```
Returns information about the string. Same as fiobj_str_info().
_Symbol type:_ `function`
#### `fiobj_hash_set2`
```c
inline FIOBJ FIO_NAME(fiobj_hash_set2, set2)(FIOBJ hash, const char *key, size_t len, FIOBJ value)
```
Sets a value in a hash map, allocating the key String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj_hash_get2`
```c
inline FIOBJ FIO_NAME(fiobj_hash_get2, get2)(FIOBJ hash, const char *buf, size_t len)
```
Finds a value in the hash map, using a temporary String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj_hash_remove2`
```c
inline int FIO_NAME(fiobj_hash_remove2, remove2)(FIOBJ hash, const char *buf, size_t len, FIOBJ *old)
```
Removes a value in a hash map, using a temporary String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj2json`
```c
inline FIOBJ fiobj2json(FIOBJ dest, FIOBJ o, uint8_t beautify)
```
Returns a JSON valid FIOBJ String, representing the object.
If `dest` is an existing String, the formatted JSON data will be appended to
the existing string.
_Symbol type:_ `function`
#### `fiobj_hash_update_json`
```c
size_t FIO_NAME(fiobj_hash_update_json, update_json)(FIOBJ hash, fio_str_info_s str)
```
Updates a Hash using JSON data.
Parsing errors and non-dictionary object JSON data are silently ignored,
attempting to update the Hash as much as possible before any errors
encountered.
Conflicting Hash data is overwritten (preferring the new over the old).
Returns the number of bytes consumed. On Error, 0 is returned and no data is
consumed.
_Symbol type:_ `function`
#### `fiobj_hash_update_json2`
```c
inline size_t FIO_NAME(fiobj_hash_update_json2, update_json2)(FIOBJ hash, char *ptr, size_t len)
```
Helper function, calls `fiobj_hash_update_json` with string information
_Symbol type:_ `function`
#### `fiobj_json_parse`
```c
FIOBJ fiobj_json_parse(fio_str_info_s str, size_t *consumed)
```
Parses a C string for JSON data. If `consumed` is not NULL, the `size_t`
variable will contain the number of bytes consumed before the parser stopped
(due to either error or end of a valid JSON data segment).
Returns a FIOBJ object matching the JSON valid C string `str`.
If the parsing failed (no complete valid JSON data) `FIOBJ_INVALID` is
returned.
_Symbol type:_ `function`
#### `fiobj_json_parse2`
```c
#define fiobj_json_parse2(data_, len_, consumed) \
fiobj_json_parse(FIO_STR_INFO2(data_, len_), consumed)
```
Helper macro, calls `fiobj_json_parse` with string information
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fiobj_json_find`
```c
FIOBJ fiobj_json_find(FIOBJ object, fio_str_info_s notation)
```
Uses JavaScript style notation to find data in an object structure.
For example, "[0].name" will return the "name" property of the first object
in an array object.
Returns a temporary reference to the object or FIOBJ_INVALID on an error.
Use `fiobj_dup` to collect an actual reference to the returned object.
_Symbol type:_ `function`
#### `fiobj_json_find2`
```c
#define fiobj_json_find2(object, str, length) \
fiobj_json_find(object, FIO_STR_INFO2(str, length))
```
Uses JavaScript style notation to find data in an object structure.
For example, "[0].name" will return the "name" property of the first object
in an array object.
Returns a temporary reference to the object or FIOBJ_INVALID on an error.
Use `fiobj_dup` to collect an actual reference to the returned object.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fiobj_mustache_build`
```c
inline FIOBJ fiobj_mustache_build(fio_mustache_s *m, FIOBJ ctx)
```
Builds a Mustache template using a FIOBJ context (usually a Hash).
Returns a FIOBJ String with the rendered template. May return `FIOBJ_INVALID`
if nothing was written.
_Symbol type:_ `function`
#### `fiobj_mustache_build2`
```c
inline FIOBJ fiobj_mustache_build2(fio_mustache_s *m, FIOBJ dest, FIOBJ ctx)
```
Builds a Mustache template using a FIOBJ context (usually a Hash).
Writes output to `dest` string (may be `FIOBJ_INVALID` / `NULL`).
Returns `dest` (or a new String). May return `FIOBJ_INVALID` if nothing was
written and `dest` was empty.
_Symbol type:_ `function`
#### `fiobj_hash_update`
```c
inline void FIO_NAME(fiobj_hash_update, update)(FIOBJ dest, FIOBJ src)
```
Updates a hash using information from another Hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/401 io api.h`
104 public symbols.
### Macros
#### `FIO_IO_BUFFER_PER_WRITE`
```c
#define FIO_IO_BUFFER_PER_WRITE 65536U
```
Control the size of the on-stack buffer used for `write` events.
_Symbol type:_ `macro`
#### `FIO_IO_THROTTLE_LIMIT`
```c
#define FIO_IO_THROTTLE_LIMIT 2097152U
```
IO will be throttled (no `on_data` events) if outgoing buffer is large.
_Symbol type:_ `macro`
#### `FIO_IO_TIMEOUT_MAX`
```c
#define FIO_IO_TIMEOUT_MAX 300000
```
Controls the maximum and default timeout in milliseconds (5 minutes).
_Symbol type:_ `macro`
#### `FIO_IO_SHUTDOWN_TIMEOUT`
```c
#define FIO_IO_SHUTDOWN_TIMEOUT 15000
```
_Symbol type:_ `macro`
#### `FIO_IO_COUNT_STORAGE`
```c
#define FIO_IO_COUNT_STORAGE 1
```
_Symbol type:_ `macro`
#### `FIO_IO_ASYN_INIT`
```c
#define FIO_IO_ASYN_INIT \
{ 0 }
```
Initializes an IO Async Queue (multi-threaded task queue).
The queue automatically spawns threads and shuts down as the IO reactor
starts or stops.
It is recommended that the `fio_io_async_s` be allocated as a static
variable, as its memory must remain valid throughout the lifetime of the
IO reactor's app.
_Symbol type:_ `macro`
### Types
#### `fio_io_protocol_s`
```c
struct fio_io_protocol_s
```
The main protocol object type. See `struct fio_io_protocol_s`.
_Symbol type:_ `type`
#### `fio_io_functions_s`
```c
struct fio_io_functions_s
```
The IO functions used by the protocol object.
_Symbol type:_ `type`
#### `fio_io_s`
```c
struct fio_io_s
```
The main IO object type. Should be treated as an opaque pointer.
_Symbol type:_ `type`
#### `fio_io_tls_s`
```c
struct fio_io_tls_s
```
An opaque type used for the SSL/TLS helper functions.
_Symbol type:_ `type`
#### `fio_pubsub_msg_s`
```c
struct fio_pubsub_msg_s
```
Message structure, as received by the `on_message` subscription callback.
_Symbol type:_ `type`
#### `fio_io_async_s`
```c
struct fio_io_async_s
```
The IO Async Queue type.
_Symbol type:_ `type`
#### `fio_io_listen_args_s`
```c
struct fio_io_listen_args_s {
/**
* The binding address in URL format. Defaults to: tcp://0.0.0.0:3000
*
* Note: `.url` accept an optional query for building a TLS context.
*
* Possible query values include:
*
* - `tls` or `ssl` (no value): sets TLS as active, possibly self-signed.
* - `tls=` or `ssl=`: value is a prefix for "key.pem" and "cert.pem".
* - `key=` and `cert=`: file paths for ".pem" files.
*
* i.e.:
*
* fio_io_listen(.url = "0.0.0.0:3000/?tls", ...);
* fio_io_listen(.url = "0.0.0.0:3000/?tls=./", ...);
* // same as:
* fio_io_listen(.url = "0.0.0.0:3000/"
* "?key=./key.pem"
* "&cert=./cert.pem", ...);
*/
const char *url;
/** The `fio_io_protocol_s` that will be assigned to incoming
* connections. */
fio_io_protocol_s *protocol;
/** The default `udata` set for (new) incoming connections. */
void *udata;
/** TLS object used for incoming connections (ownership moved to listener). */
fio_io_tls_s *tls;
/**
* Called when the a listening socket starts to listen.
*
* May be called multiple times (i.e., if the IO reactor stops and restarts).
*/
void (*on_start)(fio_io_protocol_s *protocol, void *udata);
/**
* Called during listener cleanup.
*
* This will be called separately for every process before exiting.
*/
void (*on_stop)(fio_io_protocol_s *protocol, void *udata);
/**
* Selects a queue that will be used to schedule a pre-accept task.
* May be used to test user thread stress levels before accepting connections.
*/
fio_io_async_s *queue_for_accept;
/** When forking the IO reactor - limits `listen` to the root process. */
uint8_t on_root;
/** Hides "started/stopped listening" messages from log (if set). */
uint8_t hide_from_log;
}
```
Arguments for the fio_io_listen function
_Symbol type:_ `type`
#### `fio_io_listener_s`
```c
struct fio_io_listener_s
```
_Symbol type:_ `type`
#### `fio_io_connect_args_s`
```c
typedef struct {
/** The URL to connect to (may contain TLS hints in query / `tls` scheme). */
const char *url;
/** Connection protocol (once connection established). */
fio_io_protocol_s *protocol;
/** Called in case of a failed connection, use for cleanup.
* Always runs on the IO thread, possibly asynchronously after
* `fio_io_connect` returns (even when it returns `NULL`). */
void (*on_failed)(fio_io_protocol_s *protocol, void *udata);
/** Opaque user data (set only once connection was established). */
void *udata;
/** TLS builder object for TLS connections. */
fio_io_tls_s *tls;
/** Connection timeout in milliseconds (defaults to 30 seconds). */
uint32_t timeout;
} fio_io_connect_args_s
```
Named arguments for fio_io_connect
_Symbol type:_ `type`
#### `fio_io_write_args_s`
```c
typedef struct {
/** The buffer with the data to send (if no file descriptor) */
void *buf;
/** The file descriptor to send (if no buffer) */
intptr_t fd;
/** The length of the data to be sent. On files, 0 = the whole file. */
size_t len;
/** The length of the data to be sent. On files, 0 = the whole file. */
size_t offset;
/**
* If this is a buffer, the de-allocation function used to free it.
*
* If `copy == 0`, and `dealloc` is set, `write` will take ownership.
*
* If NULL, the buffer will NOT be de-allocated.
*/
void (*dealloc)(void *);
/** If non-zero, makes a copy of the buffer or keeps a file open. */
uint8_t copy;
} fio_io_write_args_s
```
_Symbol type:_ `type`
#### `fio_io_env_set_args_s`
```c
typedef struct {
/** A numerical type filter. Defaults to 0. Negative values are reserved. */
intptr_t type;
/** The name for the link. The name and type uniquely identify the object. */
fio_buf_info_s name;
/** The object being linked to the connection. */
void *udata;
/** A callback that will be called once the connection is closed. */
void (*on_close)(void *data);
/** Set to true (1) if the name string's life lives as long as the `env` . */
uint8_t const_name;
} fio_io_env_set_args_s
```
Named arguments for the `fio_io_env_set` function.
_Symbol type:_ `type`
#### `fio_io_env_get_args_s`
```c
typedef struct {
/** A numerical type filter. Should be the same as used with
* `fio_io_env_set` */
intptr_t type;
/** The name of the object. Should be the same as used with `fio_io_env_set`
*/
fio_buf_info_s name;
} fio_io_env_get_args_s
```
Named arguments for the `fio_io_env_unset` function.
_Symbol type:_ `type`
#### `fio_io_tls_each_s`
```c
struct fio_io_tls_each_s {
fio_io_tls_s *tls;
void *udata;
void *udata2;
int (*each_cert)(struct fio_io_tls_each_s *,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
int (*each_alpn)(struct fio_io_tls_each_s *,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int (*each_trust)(struct fio_io_tls_each_s *, const char *public_cert_file);
}
```
Arguments (and info) for `fio_io_tls_each`.
_Symbol type:_ `type`
### Functions
#### `fio_io_stop`
```c
void fio_io_stop(void)
```
Stopping the IO reactor.
_Symbol type:_ `function`
#### `fio_io_add_workers`
```c
void fio_io_add_workers(int workers)
```
Adds `workers` amount of workers to the root IO reactor process.
_Symbol type:_ `function`
#### `fio_io_start`
```c
void fio_io_start(int workers)
```
Starts the IO reactor, using optional `workers` processes. Will BLOCK!
_Symbol type:_ `function`
#### `fio_io_restart`
```c
void fio_io_restart(int workers)
```
Retiers all existing workers and restarts with the number of workers.
_Symbol type:_ `function`
#### `fio_io_restart_on_signal`
```c
void fio_io_restart_on_signal(int signal)
```
Sets a signal to listen to for a hot restart (see `fio_io_restart`).
_Symbol type:_ `function`
#### `fio_io_shutdown_timeout`
```c
size_t fio_io_shutdown_timeout(void)
```
Returns the shutdown timeout for the reactor.
_Symbol type:_ `function`
#### `fio_io_shutdown_timeout_set`
```c
size_t fio_io_shutdown_timeout_set(size_t milliseconds)
```
Sets the shutdown timeout for the reactor, returning the new value.
_Symbol type:_ `function`
#### `fio_io_is_running`
```c
int fio_io_is_running(void)
```
Returns true if IO reactor running and 0 if stopped or shutting down.
_Symbol type:_ `function`
#### `fio_io_is_master`
```c
int fio_io_is_master(void)
```
Returns true if the current process is the IO reactor's master process.
_Symbol type:_ `function`
#### `fio_io_is_worker`
```c
int fio_io_is_worker(void)
```
Returns true if the current process is an IO reactor's worker process.
_Symbol type:_ `function`
#### `fio_io_workers`
```c
uint16_t fio_io_workers(int workers_requested)
```
Returns the number or workers the IO reactor will actually run.
_Symbol type:_ `function`
#### `fio_io_pid`
```c
int fio_io_pid(void)
```
Returns current process id.
_Symbol type:_ `function`
#### `fio_io_root_pid`
```c
int fio_io_root_pid(void)
```
Returns the root / master process id.
_Symbol type:_ `function`
#### `fio_io_last_tick`
```c
int64_t fio_io_last_tick(void)
```
Returns the last millisecond when the IO reactor polled for events.
_Symbol type:_ `function`
#### `fio_io_last_tick_time`
```c
int64_t fio_io_last_tick_time(void)
```
Returns a cached real-time (wall-clock) timestamp in milliseconds,
updated each IO tick alongside `fio_io_last_tick`. Use this to avoid
repeated `clock_gettime` syscalls when an approximate wall-clock ms is
sufficient (e.g., HTTP Date headers, access-log timestamps).
_Symbol type:_ `function`
#### `fio_io_listen`
```c
fio_io_listener_s *fio_io_listen(fio_io_listen_args_s args)
```
Sets up a network service on a listening socket.
Returns a self-destructible listener handle on success or NULL on error.
NOTE: this schedules a task and should NOT be called within a PRE_START or
ON_START state callback.
NOTE: the returned listener is valid until `fio_io_listen_stop` is called.
_Symbol type:_ `function`
#### `fio_io_listen`
```c
#define fio_io_listen(...) fio_io_listen((fio_io_listen_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_listen_stop`
```c
void fio_io_listen_stop(fio_io_listener_s *l)
```
Notifies a listener to stop listening and destroys it.
_Symbol type:_ `function`
#### `fio_io_listener_protocol`
```c
fio_io_protocol_s *fio_io_listener_protocol(fio_io_listener_s *l)
```
Returns the listener's associated protocol.
_Symbol type:_ `function`
#### `fio_io_listener_udata`
```c
void *fio_io_listener_udata(fio_io_listener_s *l)
```
Returns the listener's associated `udata`.
_Symbol type:_ `function`
#### `fio_io_listener_udata_set`
```c
void *fio_io_listener_udata_set(fio_io_listener_s *l, void *new_udata)
```
Sets the listener's associated `udata`, returning the old value.
_Symbol type:_ `function`
#### `fio_io_listener_url`
```c
fio_buf_info_s fio_io_listener_url(fio_io_listener_s *l)
```
Returns the URL on which the listener is listening.
_Symbol type:_ `function`
#### `fio_io_listener_is_tls`
```c
int fio_io_listener_is_tls(fio_io_listener_s *l)
```
Returns true if the listener protocol has an attached TLS context.
_Symbol type:_ `function`
#### `fio_io_connect`
```c
fio_io_s *fio_io_connect(fio_io_connect_args_s args)
```
Connects to a specific URL, returning the `fio_io_s` IO object or `NULL`.
Note: The IO object returned is owned by the reactor. The copy returned is
valid only until the next event is processed.
Ownership: calling `fio_io_connect` transfers ownership of `protocol`,
`udata` and the connecting state to the reactor. Cleanup of these MUST be
performed only from `on_failed` (connection never established) or
`on_close` (established connection closed) - never in response to
`fio_io_connect` returning `NULL`, as the reactor retains the connecting
state until the deferred failure task runs.
`on_failed` always runs on the IO thread and may fire asynchronously,
after `fio_io_connect` returns (including when `NULL` is returned). This
implies the reactor MUST run at some point after a call, or the
transferred ownership is never released.
_Symbol type:_ `function`
#### `fio_io_connect`
```c
#define fio_io_connect(url_, ...) \
fio_io_connect((fio_io_connect_args_s){.url = url_, __VA_ARGS__})
```
Connects to a specific URL, returning the `fio_io_s` IO object or `NULL`.
Note: The IO object returned is owned by the reactor. The copy returned is
valid only until the next event is processed.
Ownership: calling `fio_io_connect` transfers ownership of `protocol`,
`udata` and the connecting state to the reactor. Cleanup of these MUST be
performed only from `on_failed` (connection never established) or
`on_close` (established connection closed) - never in response to
`fio_io_connect` returning `NULL`, as the reactor retains the connecting
state until the deferred failure task runs.
`on_failed` always runs on the IO thread and may fire asynchronously,
after `fio_io_connect` returns (including when `NULL` is returned). This
implies the reactor MUST run at some point after a call, or the
transferred ownership is never released.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_attach_fd`
```c
fio_io_s *fio_io_attach_fd(fio_socket_i fd, fio_io_protocol_s *protocol, void *udata, void *tls)
```
Attaches the socket in `fd` to the facio.io engine (reactor).
* `fd` should point to a valid socket.
* `protocol` may be the existing protocol or NULL (for partial hijack).
* `udata` is opaque user data and may be any value, including NULL.
* `tls` is a context for Transport Layer (Security) and can be used to
redirect read/write operations, as set by the protocol.
Returns NULL on error. the `fio_io_s` pointer must NOT be used except
within proper callbacks.
Ownership of `tls` transfers to the reactor when `fio_io_attach_fd` is
called, whether the call succeeds or fails - callers must not use or free
it afterwards. On failure the reactor releases it with the TLS
implementation's `free_context` (never the per-connection `cleanup`).
_Symbol type:_ `function`
#### `fio_io_protocol_set`
```c
fio_io_protocol_s *fio_io_protocol_set(fio_io_s *io, fio_io_protocol_s *protocol)
```
Sets a new protocol object. `NULL` is a valid "only-write" protocol.
_Symbol type:_ `function`
#### `fio_io_protocol`
```c
fio_io_protocol_s *fio_io_protocol(fio_io_s *io)
```
Returns a pointer to the current protocol object.
If `protocol` wasn't properly set, the pointer might be NULL or invalid.
If `protocol` wasn't attached yet, may return the previous protocol.
_Symbol type:_ `function`
#### `fio_io_buffer`
```c
void *fio_io_buffer(fio_io_s *io)
```
Returns the a pointer to the memory buffer required by the protocol.
_Symbol type:_ `function`
#### `fio_io_buffer_len`
```c
size_t fio_io_buffer_len(fio_io_s *io)
```
Returns the length of the `buf` buffer.
_Symbol type:_ `function`
#### `fio_io_udata_set`
```c
void *fio_io_udata_set(fio_io_s *io, void *udata)
```
Associates a new `udata` pointer with the IO, returning the old `udata`
_Symbol type:_ `function`
#### `fio_io_udata`
```c
void *fio_io_udata(fio_io_s *io)
```
Returns the `udata` pointer associated with the IO.
_Symbol type:_ `function`
#### `fio_io_tls_set`
```c
void *fio_io_tls_set(fio_io_s *io, void *tls)
```
Associates a new `tls` pointer with the IO, returning the old `tls`
_Symbol type:_ `function`
#### `fio_io_tls`
```c
void *fio_io_tls(fio_io_s *io)
```
Returns the `tls` pointer associated with the IO.
_Symbol type:_ `function`
#### `fio_io_fd`
```c
fio_socket_i fio_io_fd(fio_io_s *io)
```
Returns the socket file descriptor (fd) associated with the IO.
_Symbol type:_ `function`
#### `fio_io_peer_info_next`
```c
int fio_io_peer_info_next(fio_io_s *io, fio_x509_cert_s *dest)
```
Returns the next peer information item for the connection.
Iterate to inspect the peer's certificate chain (leaf certificate first),
e.g., for client certificate authentication / authorization:
fio_x509_cert_s cert = {0}; // zeroed = new loop
while (fio_io_peer_info_next(io, &cert) == 0) {
// cert.verified != 0 if the TLS backend verified the chain
// cert.chain_index == position in the chain (0 == leaf)
// cert.cn, cert.fingerprint, etc.
}
The iterator is stateless — the position is identified from `dest` alone:
a zeroed `dest` (`der.buf == NULL`) starts a new loop; otherwise iteration
continues at `dest->chain_index + 1`. Multiple loops may iterate the same
connection concurrently without interfering with each other.
Iteration is capped at 128 certificates (`chain_index` 0..127) as a
deep-nesting / DoS guard — longer chains end the loop with -1.
A `NULL` `dest` returns -1. To restart a loop, zero the struct (e.g.,
`FIO_MEMSET(&cert, 0, sizeof(cert))`).
Returns 0 while data is available, or -1 when done / unavailable (e.g.,
handshake incomplete, the peer sent no certificate, or the X509 module is
unavailable).
NOTE: the parsed fields point into memory owned by the TLS backend and
remain valid only until the next fio_io_peer_info_next call (on ANY
connection) or until the connection is closed, whichever comes first.
_Symbol type:_ `function`
#### `fio_io_touch`
```c
void fio_io_touch(fio_io_s *io)
```
Resets a socket's timeout counter.
_Symbol type:_ `function`
#### `fio_io_read`
```c
size_t fio_io_read(fio_io_s *io, void *buf, size_t len)
```
Reads data to the buffer, if any data exists. Returns the number of bytes
read.
NOTE: zero (`0`) is a valid return value meaning no data was available.
_Symbol type:_ `function`
#### `fio_io_write2`
```c
void fio_io_write2(fio_io_s *io, fio_io_write_args_s args)
```
Writes data to the outgoing buffer and schedules the buffer to be sent.
_Symbol type:_ `function`
#### `fio_io_write2`
```c
#define fio_io_write2(io, ...) \
fio_io_write2(io, (fio_io_write_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_write`
```c
#define fio_io_write(io, buf_, len_) \
fio_io_write2(io, .buf = (buf_), .len = (len_), .copy = 1)
```
Helper macro for a common fio_io_write2 (copies the buffer).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_sendfile`
```c
#define fio_io_sendfile(io, source_fd, offset_, bytes) \
fio_io_write2((io), \
.fd = (source_fd), \
.offset = (size_t)(offset_), \
.len = (bytes))
```
Sends data from a file as if it were a single atomic packet (sends up to
length bytes or until EOF is reached).
Once the file was sent, the `source_fd` will be closed using `close`.
The file will be buffered to the socket chunk by chunk, so that memory
consumption is capped.
`offset` dictates the starting point for the data to be sent and length sets
the maximum amount of data to be sent.
Closes the file on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_close`
```c
void fio_io_close(fio_io_s *io)
```
Marks the IO for closure as soon as scheduled data was sent.
_Symbol type:_ `function`
#### `fio_io_close_now`
```c
void fio_io_close_now(fio_io_s *io)
```
Marks the IO for immediate closure and destruction.
_Symbol type:_ `function`
#### `fio_io_dup`
```c
fio_io_s *fio_io_dup(fio_io_s *io)
```
Increases a IO's reference count, so it won't be automatically destroyed
when all tasks have completed.
Use this function in order to use the IO outside of a scheduled task.
This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_free`
```c
void fio_io_free(fio_io_s *io)
```
Decreases a IO's reference count, so it could be automatically destroyed
when all other tasks have completed.
Use this function once finished with a IO that was `dup`-ed.
This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_suspend`
```c
void fio_io_suspend(fio_io_s *io)
```
Suspends future `on_data` events for the IO.
_Symbol type:_ `function`
#### `fio_io_unsuspend`
```c
void fio_io_unsuspend(fio_io_s *io)
```
Listens for future `on_data` events related to the IO.
_Symbol type:_ `function`
#### `fio_io_is_suspended`
```c
int fio_io_is_suspended(fio_io_s *io)
```
Returns 1 if the IO handle was suspended.
_Symbol type:_ `function`
#### `fio_io_on_data_schedule`
```c
void fio_io_on_data_schedule(fio_io_s *io)
```
Schedules one deferred `on_data` callback for already-buffered input.
Repeated calls before delivery are coalesced. This does not bypass normal
suspension or throttling checks.
_Symbol type:_ `function`
#### `fio_io_is_open`
```c
int fio_io_is_open(fio_io_s *io)
```
Returns 1 if the IO handle is open and not marked for closure.
_Symbol type:_ `function`
#### `fio_io_backlog`
```c
size_t fio_io_backlog(fio_io_s *io)
```
Returns the approximate number of bytes in the outgoing buffer.
_Symbol type:_ `function`
#### `fio_io_noop`
```c
void fio_io_noop(fio_io_s *io)
```
Does nothing.
_Symbol type:_ `function`
#### `fio_io_defer`
```c
void fio_io_defer(void (*task)(void *, void *), void *udata1, void *udata2)
```
Schedules a task for delayed execution. This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_run_every`
```c
void fio_io_run_every(fio_timer_schedule_args_s args)
```
Schedules a timer bound task, see `fio_timer_schedule`.
_Symbol type:_ `function`
#### `fio_io_run_every`
```c
#define fio_io_run_every(...) \
fio_io_run_every((fio_timer_schedule_args_s){__VA_ARGS__})
```
Schedules a timer bound task, see `fio_timer_schedule`.
Possible "named arguments" (fio_timer_schedule_args_s members) include:
* The timer function. If it returns a non-zero value, the timer stops:
int (*fn)(void *, void *)
* Opaque user data:
void *udata1
* Opaque user data:
void *udata2
* Called when the timer is done (finished):
void (*on_stop)(void *, void *)
* Timer interval, in milliseconds:
uint32_t every
* The number of times the timer should be performed. -1 == infinity:
int32_t repetitions
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_queue`
```c
fio_queue_s *fio_io_queue(void)
```
Returns a pointer for the IO reactor's queue.
_Symbol type:_ `function`
#### `fio_io_protocol_each`
```c
size_t fio_io_protocol_each(fio_io_protocol_s *protocol, void (*task)(fio_io_s *, void *udata2), void *udata2)
```
Performs a task for each IO in the stated protocol.
Call ONLY from the main IO thread (consider using `fio_io_defer`).
_Symbol type:_ `function`
#### `fio_io_env_get`
```c
void *fio_io_env_get(fio_io_s *io, fio_io_env_get_args_s)
```
Returns the named `udata` associated with the IO object (or `NULL`).
_Symbol type:_ `function`
#### `fio_io_env_get`
```c
#define fio_io_env_get(io, ...) \
fio_io_env_get(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Returns the named `udata` associated with the IO object (or `NULL`).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_set`
```c
void fio_io_env_set(fio_io_s *io, fio_io_env_set_args_s)
```
Links an object to a connection's lifetime / environment.
The `on_close` callback will be called once the connection has died.
If the `io` is NULL, the value will be set for the global environment.
_Symbol type:_ `function`
#### `fio_io_env_set`
```c
#define fio_io_env_set(io, ...) \
fio_io_env_set(io, (fio_io_env_set_args_s){__VA_ARGS__})
```
Links an object to a connection's lifetime, calling the `on_close` callback
once the connection has died.
If the `io` is NULL, the value will be set for the global environment, in
which case the `on_close` callback will only be called once the process
exits.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_unset`
```c
int fio_io_env_unset(fio_io_s *io, fio_io_env_get_args_s)
```
Un-links an object from the connection's lifetime, so it's `on_close`
callback will NOT be called.
Returns 0 on success and -1 if the object couldn't be found.
_Symbol type:_ `function`
#### `fio_io_env_unset`
```c
#define fio_io_env_unset(io, ...) \
fio_io_env_unset(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Un-links an object from the connection's lifetime, so it's `on_close`
callback will NOT be called.
Returns 0 on success and -1 if the object couldn't be found.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_remove`
```c
int fio_io_env_remove(fio_io_s *io, fio_io_env_get_args_s)
```
Removes an object from the connection's lifetime / environment, calling it's
`on_close` callback as if the connection was closed.
_Symbol type:_ `function`
#### `fio_io_env_remove`
```c
#define fio_io_env_remove(io, ...) \
fio_io_env_remove(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Removes an object from the connection's lifetime / environment, calling it's
`on_close` callback as if the connection was closed.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_tls_new`
```c
fio_io_tls_s *fio_io_tls_new(void)
```
Performs a `new` operation, returning a new `fio_io_tls_s` context.
_Symbol type:_ `function`
#### `fio_io_tls_from_url`
```c
fio_io_tls_s *fio_io_tls_from_url(fio_io_tls_s *target_or_null, fio_url_s url)
```
Takes a parsed URL and optional TLS target and returns a TLS if needed.
_Symbol type:_ `function`
#### `fio_io_tls_dup`
```c
fio_io_tls_s *fio_io_tls_dup(fio_io_tls_s *)
```
Performs a `dup` operation, increasing the object's reference count.
_Symbol type:_ `function`
#### `fio_io_tls_free`
```c
void fio_io_tls_free(fio_io_tls_s *)
```
Performs a `free` operation, reducing the reference count and freeing.
_Symbol type:_ `function`
#### `fio_io_tls_cert_add`
```c
fio_io_tls_s *fio_io_tls_cert_add(fio_io_tls_s *, const char *server_name, const char *public_cert_file, const char *private_key_file, const char *pk_password)
```
Adds a certificate a new SSL/TLS context / settings object (SNI support).
fio_io_tls_cert_add(tls, "www.example.com",
"public_key.pem",
"private_key.pem", NULL );
NOTE: Except for the `tls` and `server_name` arguments, all arguments might
be `NULL`, which a context builder (`fio_io_functions_s`) should
treat as a request for a self-signed certificate. It may be silently ignored.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_add`
```c
fio_io_tls_s *fio_io_tls_alpn_add(fio_io_tls_s *tls, const char *protocol_name, void (*on_selected)(fio_io_s *))
```
Adds an ALPN protocol callback to the SSL/TLS context.
The first protocol added will act as the default protocol to be selected.
A `NULL` protocol name will be silently ignored.
A `NULL` callback (`on_selected`) will be silently replaced with a no-op.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_select`
```c
int fio_io_tls_alpn_select(fio_io_tls_s *tls, const char *protocol_name, size_t name_length, fio_io_s *)
```
Calls the `on_selected` callback for the `fio_io_tls_s` object.
_Symbol type:_ `function`
#### `fio_io_tls_trust_add`
```c
fio_io_tls_s *fio_io_tls_trust_add(fio_io_tls_s *, const char *public_cert_file)
```
Adds a certificate to the "trust" list, which automatically adds a peer
verification requirement.
If `public_cert_file` is `NULL`, implementation is expected to add the
system's default trust registry.
Note: when the `fio_io_tls_s` object is used for server connections, this
should limit connections to clients that connect using a trusted certificate.
fio_io_tls_trust_add(tls, "google-ca.pem" );
_Symbol type:_ `function`
#### `fio_io_tls_cert_count`
```c
uintptr_t fio_io_tls_cert_count(fio_io_tls_s *tls)
```
Returns the number of `fio_io_tls_cert_add` instructions.
This could be used when deciding if to add a NULL instruction (self-signed).
If `fio_io_tls_cert_add` was never called, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_count`
```c
uintptr_t fio_io_tls_alpn_count(fio_io_tls_s *tls)
```
Returns the number of registered ALPN protocol names.
This could be used when deciding if protocol selection should be delegated to
the ALPN mechanism, or whether a protocol should be immediately assigned.
If no ALPN protocols are registered, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_trust_count`
```c
uintptr_t fio_io_tls_trust_count(fio_io_tls_s *tls)
```
Returns the number of `fio_io_tls_trust_add` instructions.
This could be used when deciding if to disable peer verification or not.
If `fio_io_tls_trust_add` was never called, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_each`
```c
int fio_io_tls_each(fio_io_tls_each_s)
```
Calls callbacks for certificate, trust certificate and ALPN added.
_Symbol type:_ `function`
#### `fio_io_tls_each`
```c
#define fio_io_tls_each(tls_, ...) \
fio_io_tls_each(((fio_io_tls_each_s){.tls = tls_, __VA_ARGS__}))
```
`fio_io_tls_each` helper macro, see `fio_io_tls_each_s` for named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_tls_default_functions`
```c
fio_io_functions_s fio_io_tls_default_functions(fio_io_functions_s *)
```
If `NULL` returns current default, otherwise sets it. Set before start.
_Symbol type:_ `function`
#### `fio_io_async_queue`
```c
inline fio_queue_s *fio_io_async_queue(fio_io_async_s *q)
```
Returns the current task queue associated with the IO Async Queue.
_Symbol type:_ `function`
#### `fio_io_async_attach`
```c
void fio_io_async_attach(fio_io_async_s *q, uint32_t threads)
```
Attaches an IO Async Queue for use in multi-threaded (non IO) tasks.
This function can be called multiple times for the same (or other) queue, as
long as the async queue (`fio_io_async_s`) was previously initialized using
`FIO_IO_ASYN_INIT` or zeroed out. i.e.:
static fio_io_async_s SLOW_HTTP_TASKS = FIO_IO_ASYN_INIT;
fio_io_async_attach(&SLOW_HTTP_TASKS, 32);
_Symbol type:_ `function`
#### `fio_io_async`
```c
#define fio_io_async(q_, ...) fio_queue_push((q_)->q, __VA_ARGS__)
```
Pushes a task to an IO Async Queue (macro helper).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_async_every`
```c
void fio_io_async_every(fio_io_async_s *q, fio_timer_schedule_args_s)
```
Schedules a timer bound task for the async queue (`fio_timer_schedule`).
_Symbol type:_ `function`
#### `fio_io_async_every`
```c
#define fio_io_async_every(async, ...) \
fio_io_async_every(async, (fio_timer_schedule_args_s){__VA_ARGS__})
```
Schedules a timer bound task, for the async queue, see `fio_timer_schedule`.
Possible "named arguments" (fio_timer_schedule_args_s members) include:
* The timer function. If it returns a non-zero value, the timer stops:
int (*fn)(void *, void *)
* Opaque user data:
void *udata1
* Opaque user data:
void *udata2
* Called when the timer is done (finished):
void (*on_stop)(void *, void *)
* Timer interval, in milliseconds:
uint32_t every
* The number of times the timer should be performed. -1 == infinity:
int32_t repetitions
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/404 ipc.h`
41 public symbols.
### Macros
#### `FIO_IPC_URL_MAX_LENGTH`
```c
#define FIO_IPC_URL_MAX_LENGTH 1024
```
_Symbol type:_ `macro`
#### `FIO_IPC_MAX_LENGTH`
```c
#define FIO_IPC_MAX_LENGTH (128ULL * 1024U * 1024U)
```
_Symbol type:_ `macro`
#### `FIO_IPC_DATA`
```c
#define FIO_IPC_DATA(...) \
(fio_buf_info_s[]) { \
__VA_ARGS__, { .len = ((size_t)-1) } \
}
```
A helper macro for composing multiple buffers into the request. Fill with
fio_buf_info_s - will stop when a buffer has zero length. Use:
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"example:"),
FIO_BUF_INFO2(&number, sizeof(number)))
_Symbol type:_ `macro`
#### `FIO_IPC_EXCLUDE_SELF`
```c
#define FIO_IPC_EXCLUDE_SELF ((fio_io_s *)((char *)-1LL))
```
Excludes current process from fio_ipc_local (to be set in ipc->from)
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_ENCRYPTED`
```c
#define FIO_IPC_FLAG_ENCRYPTED ((uint16_t)1UL << 0)
```
Set if message is in its encrypted state - do NOT edit manually
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_DONE`
```c
#define FIO_IPC_FLAG_DONE ((uint16_t)1UL << 1)
```
If set, calls `on_done` rather than `call` - requires FIO_IPC_FLAG_REPLY
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_OPCODE`
```c
#define FIO_IPC_FLAG_OPCODE ((uint16_t)1UL << 2)
```
Set if message callbacks are mapped using op-codes
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_WORKERS`
```c
#define FIO_IPC_FLAG_WORKERS ((uint16_t)1UL << 3)
```
If set, delivered to worker processes as well as master
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_CLUSTER`
```c
#define FIO_IPC_FLAG_CLUSTER ((uint16_t)1UL << 4)
```
If set, delivered to remote machines - requires FIO_IPC_FLAG_OPCODE
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_REPLY`
```c
#define FIO_IPC_FLAG_REPLY ((uint16_t)1UL << 5)
```
If set, calls `on_reply` rather than `call`
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_PING`
```c
#define FIO_IPC_FLAG_PING ((uint16_t)1UL << 6)
```
If set, this is an internal ping/keepalive message (not dispatched to user)
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_TEST`
```c
#define FIO_IPC_FLAG_TEST(msg, flag) (((msg)->routing_flags & flag) == flag)
```
If flag is set == 1, otherwise zero
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_IF`
```c
#define FIO_IPC_FLAG_IF(bcond, flag) (((uint16_t)0UL - (bcond)) & flag)
```
If flag is set == flag, otherwise zero
_Symbol type:_ `macro`
### Types
#### `fio_ipc_s`
```c
struct fio_ipc_s {
fio_io_s *from; /* IO to caller - set by receiver (unsent) */
/* ----- wire format starts here ----- */
uint32_t len; /* Length of data[] (AAD - authenticated, unencrypted) */
uint16_t flags; /* User settable flags (AAD - authenticated, unencrypted) */
uint16_t routing_flags; /* Internal (AAD - authenticated, unencrypted) */
uint64_t timestamp; /* timestamp (unencrypted, used for nonce) */
uint64_t id; /* 8 random bytes (unencrypted, used for nonce) */
union {
void (*call)(struct fio_ipc_s *); /* function pointer to call (encrypted) */
uint32_t opcode; /* op-code to execute*/
};
void (*on_reply)(struct fio_ipc_s *); /* run on caller (encrypted) */
void (*on_done)(struct fio_ipc_s *); /* run on caller (encrypted) */
void *udata; /* opaque, valid only in caller (encrypted) */
char data[]; /* Variable-length data + 16-byte MAC at end (encrypted) */
}
```
IPC message structure (reference counted)
_Symbol type:_ `type`
#### `fio_ipc_args_s`
```c
typedef struct {
void (*call)(fio_ipc_s *); /* function to call */
void (*on_reply)(fio_ipc_s *); /* (optional) reply callback */
void (*on_done)(fio_ipc_s *); /* (optional) reply finished callback */
fio_io_s *exclude; /* (optional) IO to exclude from delivery */
uint64_t timestamp; /* (optional) to force timestamp */
uint64_t id; /* (optional) to force an id value */
uint32_t opcode; /* replaces `call` with op-code if non-zero */
uint16_t flags; /* (optional) user-opaque flags */
bool cluster; /* if set, this is intended for all machines in cluster */
bool workers; /* if set, this is intended for master + workers */
void *udata; /* opaque pointer data for reply */
fio_buf_info_s *data; /* payload (see FIO_IPC_DATA) */
} fio_ipc_args_s
```
IPC call arguments
_Symbol type:_ `type`
#### `fio_ipc_opcode_s`
```c
struct fio_ipc_opcode_s {
uint32_t opcode; /* Unique op-code value */
void (*call)(struct fio_ipc_s *); /* function to call */
void (*on_reply)(struct fio_ipc_s *); /* (optional) reply callback */
void (*on_done)(struct fio_ipc_s *); /* (optional) reply finished callback */
void *udata; /* opaque, valid only in caller (encrypted) */
}
```
_Symbol type:_ `type`
#### `fio_ipc_reply_args_s`
```c
typedef struct {
fio_ipc_s *ipc;
fio_buf_info_s *data;
uint64_t timestamp; /* (optional) override timestamp, 0 = use current time */
uint64_t id; /* (optional) override id, 0 = use original request id */
uint16_t flags; /* (optional) override flags, 0 = use original request */
uint8_t done;
uint8_t flags_set; /* set to 1 if flags should be used (allows flags=0) */
} fio_ipc_reply_args_s
```
IPC reply arguments
_Symbol type:_ `type`
### Functions
#### `fio_ipc_url`
```c
const char *fio_ipc_url(void)
```
Returns the IPC url to listen to (for incoming connections).
_Symbol type:_ `function`
#### `fio_ipc_url_set`
```c
int fio_ipc_url_set(const char *url)
```
Sets the IPC url to listen to (for incoming connections).
Can only be called on the master process and only before the IO reactor.
_Symbol type:_ `function`
#### `fio_ipc_opcode_register`
```c
int fio_ipc_opcode_register(fio_ipc_opcode_s opcode)
```
Registers an op-code for message routing.
There are two types of messages:
1. Fast Path - function pointers in the message payload (local IPC only).
2. Safe Path - Op-Code in the `call` payload (multi-machine RCP).
Op-Codes MUST be non-zero uint32_t values.
RESERVED: op-codes >= 0xFF000000 are reserved for internal use.
Note: Thread safety requires that this be called before fio_io_start.
Returns -1 or failure (not on master process / already registered)
_Symbol type:_ `function`
#### `fio_ipc_opcode_register`
```c
#define fio_ipc_opcode_register(...) \
fio_ipc_opcode_register((fio_ipc_opcode_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_opcode`
```c
const fio_ipc_opcode_s *fio_ipc_opcode(uint32_t opcode)
```
Returns a pointer to a registered op-code, or NULL if missing.
_Symbol type:_ `function`
#### `fio_ipc_call`
```c
#define fio_ipc_call(...) fio_ipc_send(fio_ipc_new(__VA_ARGS__))
```
Call arbitrary code in master process (worker → master / master → master).
The `call` function pointer is executed on the master process.
Replies are sent back to the caller via `on_reply` callback.
When all replies are done, `on_done` is called.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_local`
```c
#define fio_ipc_local(...) fio_ipc_send(fio_ipc_new(.workers = 1, __VA_ARGS__))
```
Call arbitrary code in master process and workers (all local).
Replies can be sent back to the caller only from its local master process.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_cluster`
```c
#define fio_ipc_cluster(...) \
fio_ipc_send(fio_ipc_new(.cluster = 1, __VA_ARGS__))
```
Call arbitrary code in master process of every machine in cluster.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_broadcast`
```c
#define fio_ipc_broadcast(...) \
fio_ipc_send(fio_ipc_new(.workers = 1, .cluster = 1, __VA_ARGS__))
```
Call arbitrary code in master process of every machine in cluster.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_reply`
```c
void fio_ipc_reply(fio_ipc_reply_args_s args)
```
Send a response to the caller process (master → caller).
Can be called multiple times for streaming responses.
Set `done = 1` on the last reply.
_Symbol type:_ `function`
#### `fio_ipc_reply`
```c
#define fio_ipc_reply(r, ...) \
fio_ipc_reply((fio_ipc_reply_args_s){.ipc = (r), __VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_new`
```c
fio_ipc_s *fio_ipc_new(fio_ipc_args_s args)
```
Authors a message without sending it.
Used internally but available for "faking" IPC or when composing a unified
code path for local execution.
_Symbol type:_ `function`
#### `fio_ipc_new`
```c
#define fio_ipc_new(...) fio_ipc_new((fio_ipc_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_dup`
```c
fio_ipc_s *fio_ipc_dup(fio_ipc_s *msg)
```
Duplicate a message (increment reference count).
Use when storing messages for later processing.
Every dup() must be matched with a free().
_Symbol type:_ `function`
#### `fio_ipc_free`
```c
void fio_ipc_free(fio_ipc_s *msg)
```
Free a message (decrement reference count).
Message is destroyed when reference count reaches zero.
_Symbol type:_ `function`
#### `fio_ipc_detach`
```c
void fio_ipc_detach(fio_ipc_s *msg)
```
Detaches the IPC message from it's originating IO.
Call if storing or performing non-IPC actions using the IPC message.
_Symbol type:_ `function`
#### `fio_ipc_send`
```c
void fio_ipc_send(fio_ipc_s *ipc)
```
Encrypts the IPC message(!), sends it for execution and frees it.
Message will be sent according to the flags set (see fio_ipc_new):
- Only to Master (fio_ipc_call);
- To Master and Workers (fio_ipc_local);
- To Master on Every Machine (fio_ipc_cluster);
- To Master and Workers on Every Machine (fio_ipc_broadcast);
Note: Takes ownership of the message's memory.
Note: overwrites after_send unless `ipc->from == FIO_IPC_EXCLUDE_SELF`
Note: excludes ipc->from.
Note: The message is encrypted and unusable once call returns - pass it the
last reference.
_Symbol type:_ `function`
#### `fio_ipc_send_to`
```c
void fio_ipc_send_to(fio_io_s *to, fio_ipc_s *ipc)
```
Encrypts the IPC message(!) and sends it to target IO - frees the message.
Note: Takes ownership of the message's memory.
Note: The message is encrypted and unusable until the message was sent and
either freed or the `after_send` callback was called.
_Symbol type:_ `function`
#### `fio_ipc_after_send`
```c
void fio_ipc_after_send(fio_ipc_s *ipc, void (*fn)(fio_ipc_s *, void *), void *udata)
```
_Symbol type:_ `function`
#### `fio_ipc_encrypt`
```c
void fio_ipc_encrypt(fio_ipc_s *m)
```
Encrypt IPC message before sending them anywhere.
_Symbol type:_ `function`
#### `fio_ipc_decrypt`
```c
inline int fio_ipc_decrypt(fio_ipc_s *m)
```
Decrypt IPC message when received.
_Symbol type:_ `function`
#### `fio_ipc_cluster_listen`
```c
fio_io_listener_s *fio_ipc_cluster_listen(uint16_t port)
```
Listens to cluster connections on the port listed, auto-connects to peers.
This does NOT improve message exchange or pub/sub performance. This is
designed for downtime mitigation (rotating pods) / data tunneling and client
load balancing (without message load balancing).
All server instances get all `cluster` messages (e.g., pub/sub cluster).
Note: uses the environment's (shared) secret for rudimentary encryption
without forward secrecy. Rotate secrets when possible (requires restart).
Good for trusted data centers, Kubernetes pods, etc'.
_Symbol type:_ `function`
#### `fio_ipc_cluster_connect`
```c
void fio_ipc_cluster_connect(const char *url)
```
Manually connects to cluster peers. Usually unnecessary.
_Symbol type:_ `function`
#### `fio_ipc_cluster_port`
```c
uint16_t fio_ipc_cluster_port(void)
```
Returns the last port number passed to either fio_ipc_cluster_listen or
fio_ipc_cluster_connect - zero if none.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/405 tls13.h`
1 public symbols.
### Functions
#### `fio_tls13_io_functions`
```c
fio_io_functions_s fio_tls13_io_functions(void)
```
Returns the TLS 1.3 IO functions.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/420 pubsub.h`
26 public symbols.
### Macros
#### `FIO_PUBSUB_FUTURE_LIMIT_MS`
```c
#define FIO_PUBSUB_FUTURE_LIMIT_MS 60000ULL
```
Maximum time in milliseconds to allow "future" messages to be delivered.
If the module detects someone publishing a future message, it will refuse to
deliver the message to subscribers.
Instead, the module will notify the history engines, allowing them to buffer
future messages for future delivery.
_Symbol type:_ `macro`
#### `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT`
```c
#define FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT (1ULL << 28)
```
The default cache limit - 256Mb
_Symbol type:_ `macro`
### Types
#### `fio_pubsub_subscribe_args_s`
```c
typedef struct {
/**
* The subscription owner - if none, the subscription is owned by the system.
*
* Note:
*
* Both the system and the `io` objects each manage channel listing
* which allows only a single subscription to the same channel.
*
* This means a single subscription per channel per IO and a single
* subscription per channel for the global system unless managing the
* subscription handle manually.
*/
fio_io_s *io;
/**
* A named `channel` to which the message was sent.
*
* Subscriptions require a match by both channel name and namespace filter.
*/
fio_buf_info_s channel;
/**
* The callback to be called for each message forwarded to the subscription.
*/
void (*on_message)(fio_pubsub_msg_s *msg);
/** An optional callback for when a subscription is canceled. */
void (*on_unsubscribe)(void *udata);
/** The opaque udata value is ignored and made available to the callbacks. */
void *udata;
/** The queue to which the callbacks should be routed. May be NULL. */
fio_queue_s *queue;
/**
* OPTIONAL: subscription handle return value - should be NULL when using
* automatic memory management with the IO or global environment.
*
* When set, the `io` pointer will be ignored and the subscription object
* handle will be written to the `subscription_handle_ptr` which MUST be
* used when unsubscribing.
*
* NOTE: this could cause subscriptions and memory leaks unless properly
* handled.
*/
uintptr_t *subscription_handle_ptr;
/** Replay cached messages (if any) since supplied time in milliseconds. */
uint64_t replay_since;
/** A numerical namespace `filter` subscribers need to match. */
int16_t filter;
/** If set, pattern matching will be used (name is a pattern). */
uint8_t is_pattern;
/** If set, subscription will be limited to the root / master process. */
uint8_t master_only;
} fio_pubsub_subscribe_args_s
```
_Symbol type:_ `type`
#### `fio_pubsub_publish_args_s`
```c
typedef struct {
struct fio_pubsub_engine_s const *engine;
fio_io_s *from;
uint64_t id;
uint64_t timestamp;
fio_buf_info_s channel;
fio_buf_info_s message;
int16_t filter;
} fio_pubsub_publish_args_s
```
Publish arguments
_Symbol type:_ `type`
#### `fio_pubsub_engine_s`
```c
struct fio_pubsub_engine_s {
/** Called when engine is detached */
void (*detached)(const struct fio_pubsub_engine_s *eng);
/** Called when a subscription is created */
void (*subscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a pattern subscription is created */
void (*psubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a subscription is removed */
void (*unsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a pattern subscription is removed */
void (*punsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a message is published */
void (*publish)(const struct fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg);
}
```
Engine structure for external pub/sub backends.
EXECUTION CONTEXT:
- Publish callback can be called from any thread / process.
- Subscription callbacks are called from the MASTER process only
- Subscription callbacks are called from the main event loop thread
- Callbacks MUST NOT block (defer long operations)
_Symbol type:_ `type`
#### `fio_pubsub_history_s`
```c
struct fio_pubsub_history_s {
/** Cleanup callback - called when history manager is detached */
void (*detached)(const struct fio_pubsub_history_s *hist);
/**
* Stores a message in history.
*
* Returns 0 on success, -1 on error.
* Called when a message is published (master only).
*/
int (*push)(const struct fio_pubsub_history_s *hist, fio_pubsub_msg_s *msg);
/**
* Replay messages since timestamp by calling callback for each.
*
* Returns 0 if replay was handled, -1 if this manager cannot replay.
*
* MUST call the `on_done` callback to handle possible cleanup.
*/
int (*replay)(const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter,
uint64_t since,
void (*on_message)(fio_pubsub_msg_s *msg, void *udata),
void (*on_done)(void *udata),
void *udata);
/**
* Get oldest available timestamp for a channel.
*
* Returns UINT64_MAX if no history available.
*/
uint64_t (*oldest)(const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter);
}
```
History storage interface - completely separate from engines.
EXECUTION CONTEXT:
- All callbacks are called from the MASTER process only
- Callbacks are called from the main event loop thread
- Callbacks MUST NOT block (defer long operations)
- Callbacks SHOULD NOT be called directly by the user
_Symbol type:_ `type`
### Functions
#### `fio_pubsub_subscribe`
```c
void fio_pubsub_subscribe(fio_pubsub_subscribe_args_s args)
```
Subscribe to a channel.
In worker processes, this uses IPC to notify the master.
_Symbol type:_ `function`
#### `fio_pubsub_subscribe`
```c
#define fio_pubsub_subscribe(...) \
fio_pubsub_subscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_unsubscribe`
```c
int fio_pubsub_unsubscribe(fio_pubsub_subscribe_args_s args)
```
Unsubscribe from a channel.
Returns 0 on success, -1 if subscription not found.
_Symbol type:_ `function`
#### `fio_pubsub_unsubscribe`
```c
#define fio_pubsub_unsubscribe(...) \
fio_pubsub_unsubscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_publish`
```c
void fio_pubsub_publish(fio_pubsub_publish_args_s args)
```
Publish a message to a channel.
In worker processes, this uses IPC to notify the master.
_Symbol type:_ `function`
#### `fio_pubsub_publish`
```c
#define fio_pubsub_publish(...) \
fio_pubsub_publish((fio_pubsub_publish_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_defer`
```c
void fio_pubsub_defer(fio_pubsub_msg_s *msg)
```
Pushes execution of the on_message callback to the end of the queue.
_Symbol type:_ `function`
#### `fio_pubsub_match_fn_set`
```c
void fio_pubsub_match_fn_set(uint8_t (*match_cb)(fio_str_info_s, fio_str_info_s))
```
_Symbol type:_ `function`
#### `fio_pubsub_msg2ipc`
```c
fio_ipc_s *fio_pubsub_msg2ipc(fio_pubsub_msg_s *msg)
```
Returns the underlying IPC message buffer carrying the message data.
This allows message deferral (use fio_ipc_dup) and tighter control over the
message's lifetime.
_Symbol type:_ `function`
#### `fio_pubsub_ipc2msg`
```c
fio_pubsub_msg_s fio_pubsub_ipc2msg(fio_ipc_s *ipc)
```
Extract pub/sub message from IPC message
_Symbol type:_ `function`
#### `fio_pubsub_engine_attach`
```c
void fio_pubsub_engine_attach(fio_pubsub_engine_s *engine)
```
Attach an engine to the pub/sub system - moves ownership to the system.
_Symbol type:_ `function`
#### `fio_pubsub_engine_detach`
```c
void fio_pubsub_engine_detach(fio_pubsub_engine_s *engine)
```
Detach an engine from the pub/sub system - frees system's reference.
_Symbol type:_ `function`
#### `fio_pubsub_engine_ipc`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_ipc(void)
```
Returns the builtin engine for publishing to the process group (IPC).
_Symbol type:_ `function`
#### `fio_pubsub_engine_cluster`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_cluster(void)
```
Returns the builtin engine for multi-machine cluster publishing (RPC).
_Symbol type:_ `function`
#### `fio_pubsub_engine_default`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default(void)
```
Returns the current default engine associated with the pub/sub system.
_Symbol type:_ `function`
#### `fio_pubsub_engine_default_set`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default_set( fio_pubsub_engine_s const *engine)
```
Sets the current default engine associated with the pub/sub system.
_Symbol type:_ `function`
#### `fio_pubsub_history_attach`
```c
int fio_pubsub_history_attach(const fio_pubsub_history_s *manager, uint8_t priority)
```
Attach a history manager with the given priority.
Multiple history managers can be attached. All managers receive push()
calls when messages are published. For replay(), managers are tried in
priority order (highest first) until one can handle the request.
_Symbol type:_ `function`
#### `fio_pubsub_history_detach`
```c
void fio_pubsub_history_detach(const fio_pubsub_history_s *manager)
```
Detach a history manager.
_Symbol type:_ `function`
#### `fio_pubsub_history_push_all`
```c
void fio_pubsub_history_push_all(fio_pubsub_msg_s *msg)
```
Pushes a pub/sub message to all history containers.
Use ONLY by an engine, if the message needs to be saved to the history
managers in the process but is never delivered locally.
_Symbol type:_ `function`
#### `fio_pubsub_history_cache`
```c
fio_pubsub_history_s const *fio_pubsub_history_cache(size_t size_limit)
```
Get the built-in in-memory history manager and set it's byte-size limit.
A zero value size_limit will be replaced with the following default:
- Environment value of WEBSITE_MEMORY_LIMIT_MB * 1024 * 1024
- Environment value of WEBSITE_MEMORY_LIMIT_KB
- Environment value of WEBSITE_MEMORY_LIMIT
- FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/422 redis.h`
10 public symbols.
### Macros
#### `FIO_REDIS_READ_BUFFER`
```c
#define FIO_REDIS_READ_BUFFER 65536
```
Size of the read buffer for the Redis connection.
NOTE: must fit fio_redis_connection_s.buf_pos (uint32_t).
_Symbol type:_ `macro`
#### `FIO_REDIS_MAX_BATCH`
```c
#define FIO_REDIS_MAX_BATCH 128
```
Maximum number of complete messages processed per `on_data` event.
When the cap is reached with more data buffered, processing continues in a
deferred task. This keeps any single event-loop callback small - downstream
work per message is limited to scheduling (defer / publish), never I/O.
_Symbol type:_ `macro`
### Types
#### `fio_redis_args_s`
```c
typedef struct {
/**
* Redis server URL.
*
* Supported formats:
* - "redis://host:port"
* - "redis://host" (default port 6379)
* - "host:port" (no scheme)
* - "host" (no scheme, default port 6379)
* - NULL or empty → defaults to "localhost:6379"
*/
const char *url;
/** Redis server's password, if any (folded into the HELLO 3 handshake) */
const char *auth;
/** Length of auth string (0 = auto-detect with strlen) */
size_t auth_len;
/** Ping interval in seconds (0 = default 300 seconds) */
uint8_t ping_interval;
/**
* Cumulative payload budget per top-level Redis message, in bytes.
*
* Budget = Σ(all string payload bytes) + 32 × (count of ALL objects -
* String, Array, Map, Bool, Number, etc.). Checked BEFORE allocating or
* appending; a breach logs an error and disconnects.
*
* 0 = default (16MB). Protects against hostile / corrupt servers declaring
* huge `$` allocations or oversized replies.
*/
size_t payload_limit;
} fio_redis_args_s
```
Arguments for creating a Redis engine
_Symbol type:_ `type`
#### `fio_redis_state_e`
```c
typedef enum {
/** The engine stopped after a failed HELLO handshake (or NULL was queried). */
FIO_REDIS_STATE_ERROR,
/** No socket is attached, or the RESP3 HELLO handshake is still pending. */
FIO_REDIS_STATE_CONNECTING,
/** A socket is attached and its RESP3 HELLO handshake completed. */
FIO_REDIS_STATE_CONNECTED,
} fio_redis_state_e
```
Redis connection state, observed from the IO thread.
_Symbol type:_ `type`
### Functions
#### `fio_redis_new`
```c
fio_pubsub_engine_s *fio_redis_new(fio_redis_args_s args)
```
Creates a Redis pub/sub engine with reference count = 1.
The engine is active only after the IO reactor starts running.
The caller owns the returned reference and must call fio_redis_free()
when done. Attaching to pub/sub does NOT transfer ownership.
Returns a pointer to the engine or NULL on error.
_Symbol type:_ `function`
#### `fio_redis_new`
```c
#define fio_redis_new(...) fio_redis_new((fio_redis_args_s){__VA_ARGS__})
```
Creates a Redis pub/sub engine (named arguments helper macro).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_redis_dup`
```c
fio_pubsub_engine_s *fio_redis_dup(fio_pubsub_engine_s *engine)
```
Increments the reference count and returns the engine.
Use this when you need to share the engine across multiple owners.
Each call to fio_redis_dup() must be balanced with fio_redis_free().
_Symbol type:_ `function`
#### `fio_redis_free`
```c
void fio_redis_free(fio_pubsub_engine_s *engine)
```
Releases the caller's reference to the engine.
This function simply decrements the reference count. If the ref reaches 0
(no other refs held), fio___redis_destroy() fires immediately:
sets running=0, closes connections, drains the command queue, frees memory.
The engine stays alive as long as any other ref is held (e.g. the pub/sub
system's ref taken via subscribe/psubscribe). The pub/sub system releases
its ref via the on_detached callback when fio_pubsub_engine_detach() fires.
The caller does NOT need to call fio_pubsub_engine_detach() before freeing.
Calling fio_pubsub_engine_detach() before fio_redis_free() is also safe
(detach releases the system ref; free releases the caller ref).
Safe to call with NULL (no-op).
_Symbol type:_ `function`
#### `fio_redis_state`
```c
fio_redis_state_e fio_redis_state(fio_pubsub_engine_s const *engine)
```
Returns the current Redis connection state.
This function must be called from the IO thread. Commands can be sent in
every state: they queue until the RESP3 HELLO handshake completes.
_Symbol type:_ `function`
#### `fio_redis_send`
```c
int fio_redis_send(fio_pubsub_engine_s *engine, FIOBJ command, void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply, void *udata), void *udata)
```
Sends a Redis command through the engine's connection.
The response will be sent back using the optional callback. `udata` is passed
along untouched.
The `command` should be a FIOBJ array containing the command and arguments.
Note: NEVER call Pub/Sub commands (SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE,
PUNSUBSCRIBE) using this function, as it will violate the Redis connection's
protocol.
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/430 http api.h`
153 public symbols.
### Macros
#### `FIO_HTTP_DEFAULT_MAX_HEADER_SIZE`
```c
#define FIO_HTTP_DEFAULT_MAX_HEADER_SIZE 32768 /* (1UL << 15) */
```
The default HTTP total header size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_MAX_LINE_LEN`
```c
#define FIO_HTTP_DEFAULT_MAX_LINE_LEN 8192 /* (1UL << 13) */
```
The default HTTP header line limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_MAX_BODY_SIZE`
```c
#define FIO_HTTP_DEFAULT_MAX_BODY_SIZE 33554432 /* (1UL << 25) */
```
The default HTTP payload size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE`
```c
#define FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE 262144 /* (1UL << 18) */
```
The default WebSocket message size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_TIMEOUT`
```c
#define FIO_HTTP_DEFAULT_TIMEOUT 50
```
The default timeout for HTTP connections.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_TIMEOUT_LONG`
```c
#define FIO_HTTP_DEFAULT_TIMEOUT_LONG 50
```
The default timeout for long held HTTP connections (WebSockets / SSE).
_Symbol type:_ `macro`
#### `FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER`
```c
#define FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER 0
```
Adds a "content-length" header to the HTTP handle (usually redundant).
_Symbol type:_ `macro`
#### `FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT`
```c
#define FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT ((1UL << 16) - 10UL)
```
UTF-8 validity tests will be performed only for data shorter than this.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATS`
```c
#define FIO_WEBSOCKET_STATS 0
```
If true, logs longest WebSocket round-trips (using FIO_LOG_INFO).
_Symbol type:_ `macro`
#### `FIO_HTTP_WEBSOCKET_DEFLATE_MIN`
```c
#define FIO_HTTP_WEBSOCKET_DEFLATE_MIN 1024
```
Messages smaller than this are not compressed (fits in a single TCP/IP
packet, compression saves no network overhead).
_Symbol type:_ `macro`
#### `FIO_HTTP_EXACT_LOGGING`
```c
#define FIO_HTTP_EXACT_LOGGING 0
```
By default, facil.io logs the HTTP request cycle using a fuzzy starting and
ending point for the time stamp.
The fuzzy timestamp includes delays that aren't related to the HTTP request
and may ignore time passed due to timestamp caching.
On the other hand, `FIO_HTTP_EXACT_LOGGING` collects exact time stamps to
measure the time it took to process the HTTP request (excluding time spent
reading / writing the data from the network).
Due to the preference to err on the side of higher performance, fuzzy
time-stamping is the default.
_Symbol type:_ `macro`
#### `FIO_HTTP_BODY_RAM_LIMIT`
```c
#define FIO_HTTP_BODY_RAM_LIMIT (1 << 17)
```
The HTTP handle automatically switches between RAM storage and file storage
once the HTTP body (payload) reaches a certain size. This control this point
of transition
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_LIMIT`
```c
#define FIO_HTTP_CACHE_LIMIT 0 /* ((1UL << 6) + (1UL << 5)) */
```
Each of the HTTP String Caches will be limited to this String count.
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_STR_MAX_LEN`
```c
#define FIO_HTTP_CACHE_STR_MAX_LEN (1 << 12)
```
The HTTP handle will avoid caching strings longer than this value.
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_USES_MUTEX`
```c
#define FIO_HTTP_CACHE_USES_MUTEX 1
```
The HTTP cache will use a mutex to allow headers to be set concurrently.
_Symbol type:_ `macro`
#### `FIO_HTTP_PRE_CACHE_KNOWN_HEADERS`
```c
#define FIO_HTTP_PRE_CACHE_KNOWN_HEADERS 1
```
Adds a static cache for common HTTP header names.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_INDEX_FILENAME`
```c
#define FIO_HTTP_DEFAULT_INDEX_FILENAME "index"
```
The default file name when a static file response points to a folder.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATIC_FILE_COMPLETION`
```c
#define FIO_HTTP_STATIC_FILE_COMPLETION 1
```
Attempts to auto-complete static file paths with missing extensions.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT`
```c
#define FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT (1UL << 21) /* 2 MiB */
```
Maximum file size (in bytes) for on-disk static file compression.
_Symbol type:_ `macro`
#### `FIO_HTTP_LOG_X_REQUEST_START`
```c
#define FIO_HTTP_LOG_X_REQUEST_START 1
```
_Symbol type:_ `macro`
#### `FIO_HTTP_ENFORCE_LOWERCASE_HEADERS`
```c
#define FIO_HTTP_ENFORCE_LOWERCASE_HEADERS 0
```
If true, the HTTP handle will copy input header names to lower case.
_Symbol type:_ `macro`
#### `FIO_HTTP_PATH_EACH`
```c
#define FIO_HTTP_PATH_EACH(path, pos)
```
Loops over each section of the path, decrypting percent encoding as
necessary.
The macro accepts the following:
- `path`: the path string - accessible using fio_http_path(h).
- `pos` : the name of the variable to use for accessing the section.
The variable `pos` is a `fio_buf_info_s`.
**Note**: the macro will break if a path's section length is greater than
(about) 4063 bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_HEADER_EACH_VALUE`
```c
#define FIO_HTTP_HEADER_EACH_VALUE(http_handle, is_request, header_name, pos) \
for (fio_http_header_each_s pos = fio_http_header_each_first((http_handle), \
(is_request), \
(header_name)); \
pos.value.buf; \
fio_http_header_each_next(&pos, (http_handle)))
```
Iterate repeated request (`is_request = 1`) or response (`0`) header
fields and their comma-separated values; `pos` is a chosen cursor name.
SP / HTAB around each value is trimmed. Use `.value.buf` and `.value.len`,
not string functions, for the non-NUL-terminated slice. Do not mutate the
handle while iterating. `break` / `continue` work as in a regular for loop.
Example:
FIO_HTTP_HEADER_EACH_VALUE(h, 1, FIO_STR_INFO1("accept"), item) {
FIO_HTTP_HEADER_EACH_PROPERTY(item, property) {
// property.name and property.value are length-delimited slices.
}
}
_Symbol type:_ `macro`
#### `FIO_HTTP_HEADER_EACH_PROPERTY`
```c
#define FIO_HTTP_HEADER_EACH_PROPERTY(value_, pos) \
for (fio_http_header_each_s pos = \
fio_http_header_each_property_first(&(value_).properties); \
pos.value.buf; \
fio_http_header_each_property_next(&pos))
```
Iterate semicolon-separated properties for a value cursor; `pos` is a
chosen name. Names and values are trimmed of surrounding SP / HTAB.
A missing `=` or an empty right-hand side yields `value.len == 0` with a
non-NULL `value.buf`; stop iteration only when `value.buf == NULL`.
Quoted strings remain quoted, without decoding. `break` / `continue` work.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_STREAMING`
```c
#define FIO_HTTP_STATE_STREAMING 1
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_FINISHED`
```c
#define FIO_HTTP_STATE_FINISHED 2
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_UPGRADED`
```c
#define FIO_HTTP_STATE_UPGRADED 4
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_WEBSOCKET`
```c
#define FIO_HTTP_STATE_WEBSOCKET 8
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_SSE`
```c
#define FIO_HTTP_STATE_SSE 16
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_COOKIES_PARSED`
```c
#define FIO_HTTP_STATE_COOKIES_PARSED 32
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_FREEING`
```c
#define FIO_HTTP_STATE_FREEING 64
```
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_DYNAMIC`
```c
#define FIO_HTTP_CFLAG_COMPRESS_DYNAMIC 1
```
Controller flags (cflags) for opt-in compression features.
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_WS`
```c
#define FIO_HTTP_CFLAG_COMPRESS_WS 2
```
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_STATIC`
```c
#define FIO_HTTP_CFLAG_COMPRESS_STATIC 4
```
_Symbol type:_ `macro`
### Types
#### `fio_http_s`
```c
struct fio_http_s
```
The HTTP Handle type.
Note that the type is NOT designed to be thread-safe.
_Symbol type:_ `type`
#### `fio_http_controller_s`
```c
struct fio_http_controller_s
```
The HTTP Controller points to all the callbacks required by the HTTP Handler.
This allows the HTTP Handler to be somewhat protocol agnostic.
Note: if the controller callbacks aren't thread-safe, than the `http_write`
function MUST NOT be called from any thread except the thread that the
controller is expecting.
_Symbol type:_ `type`
#### `fio_http_settings_s`
```c
struct fio_http_settings_s {
/** Called before body uploads, when a client sends an `Expect` header. */
void (*pre_http_body)(fio_http_s *h);
/** Callback for HTTP requests (server) or responses (client). */
void (*on_http)(fio_http_s *h);
/** Called when a request / response cycle is finished (for WebSocket /
* SSE connections, called after `on_close`, when the connection closes). */
void (*on_finish)(fio_http_s *h);
/** Authenticate EventSource (SSE) requests, return non-zero to deny.*/
int (*on_authenticate_sse)(fio_http_s *h);
/** Authenticate WebSockets Upgrade requests, return non-zero to deny.*/
int (*on_authenticate_websocket)(fio_http_s *h);
/** Called once a WebSocket / SSE connection upgrade is complete. */
void (*on_open)(fio_http_s *h);
/** Called when a WebSocket message is received. */
void (*on_message)(fio_http_s *h, fio_buf_info_s msg, uint8_t is_text);
/** Called when an EventSource event is received. */
void (*on_eventsource)(fio_http_s *h,
fio_buf_info_s id,
fio_buf_info_s event,
fio_buf_info_s data);
/** Called when an EventSource reconnect event requests an ID. */
void (*on_eventsource_reconnect)(fio_http_s *h, fio_buf_info_s id);
/** Called for WebSocket / SSE connections when outgoing buffer is empty. */
void (*on_ready)(fio_http_s *h);
/** Called for open WebSocket / SSE connections during shutting down. */
void (*on_shutdown)(fio_http_s *h);
/** Called after a WebSocket / SSE connection is closed (for cleanup). */
void (*on_close)(fio_http_s *h);
/** (optional) the callback to be performed when the HTTP service closes. */
void (*on_stop)(struct fio_http_settings_s *settings);
/** Default opaque user data for HTTP handles (fio_http_s). */
void *udata;
/** Optional SSL/TLS support. */
fio_io_functions_s *tls_io_func;
/** Optional SSL/TLS support. */
fio_io_tls_s *tls;
/** Optional HTTP task queue (for multi-threading HTTP responses) */
fio_io_async_s *queue;
/**
* A public folder for file transfers - allows to circumvent any
* application layer logic and simply serve static files.
*
* Static file responses are attempted for `GET` and `HEAD` requests
* only (RFC 9110 §9.3 - a static file is not a valid response to other
* methods); any other method is forwarded to `on_http` (which may serve
* files explicitly by calling `fio_http_static_file_response`). On a
* miss the request is also forwarded to `on_http`. Folders resolve to
* their `index` file and missing extensions are auto-completed
* (`.html`, `.htm`, `.txt`, `.md`) - see
* `FIO_HTTP_STATIC_FILE_COMPLETION`.
*
* Pre-compressed variants are supported: when the client's
* `Accept-Encoding` allows it, an up-to-date `file.br`, `file.zstd`,
* `file.gz` or `file.zip` variant (preference order: `br`, `zstd`,
* `gzip`, `deflate`) is served instead of the original file. With
* `compress_static`, missing `.br` / `.gz` variants are also created on
* demand (written into this folder). Ranged requests are always served
* identity (no variant selection).
*
* The folder must exist when the listener starts, otherwise the setting
* is ignored (with an error log).
*/
fio_str_info_s public_folder;
/**
* The max-age value (in seconds) for caching static files sent from
* `public_folder`.
*
* Defaults to 0 (the `Cache-Control` header is not sent).
*
* Note: NOT inherited by routes - a route that serves static files must
* set its own `max_age`.
*/
size_t max_age;
/**
* The maximum total of bytes for the overall size of the request string and
* headers, combined.
*
* Defaults to FIO_HTTP_DEFAULT_MAX_HEADER_SIZE bytes.
*/
uint32_t max_header_size;
/**
* The maximum number of bytes allowed per header / request line.
*
* Defaults to FIO_HTTP_DEFAULT_MAX_LINE_LEN bytes.
*/
uint32_t max_line_len;
/**
* The maximum size of an HTTP request's body (posting / downloading).
*
* Defaults to FIO_HTTP_DEFAULT_MAX_BODY_SIZE bytes.
*/
size_t max_body_size;
/**
* The maximum WebSocket message size/buffer (in bytes) for Websocket
* connections. Defaults to FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE bytes.
*/
size_t ws_max_msg_size;
/** reserved for future use. */
intptr_t reserved1;
/** reserved for future use. */
intptr_t reserved2;
/**
* An HTTP/1.x connection timeout.
*
* Defaults to FIO_HTTP_DEFAULT_TIMEOUT seconds.
*
* Note: the connection might be closed (by other side) before timeout occurs.
*/
uint8_t timeout;
/**
* Timeout for the WebSocket connections in seconds. Defaults to
* FIO_HTTP_DEFAULT_TIMEOUT_LONG seconds.
*
* A ping will be sent whenever the timeout is reached.
*
* Connections are only closed when a ping cannot be sent (the network layer
* fails). Pongs are ignored.
*/
uint8_t ws_timeout;
/**
* Timeout for EventSource (SSE) connections in seconds. Defaults to
* FIO_HTTP_DEFAULT_TIMEOUT_LONG seconds.
*
* A ping will be sent whenever the timeout is reached.
*
* Connections are only closed when a ping cannot be sent (the network layer
* fails).
*/
uint8_t sse_timeout;
/** Timeout for client connections (only relevant in client mode). */
uint8_t connect_timeout;
/** Logging flag - set to TRUE to log HTTP requests. */
uint8_t log;
/**
* Opt-in: auto-compress static files - missing `.br` / `.gz` variants are
* created on demand and written into the `public_folder`.
*
* Creation is limited to compressible (text-like) MIME types and file
* sizes between 1024 bytes and FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT (2
* MiB), and is skipped when compression wouldn't shrink the file.
* Variants whose modification time is older than the original file are
* re-created.
*
* Note: on-demand writes are attacker-triggerable - the public folder
* MUST be writable and quota'd. Pre-generating variants at deploy time is
* recommended.
*
* Note: per-route (read from the matching route's settings; routes
* inherit the listener's root value at route-creation). The value is a
* failure-memoization shift register, atomically shared by the IO
* threads - see `fio_http_static_file_response`. Detached handles (no
* settings) gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` handle cflag
* instead, with no memoization state.
*/
uint8_t compress_static;
/**
* Opt-in: auto-compress dynamic HTTP responses on-the-fly.
*
* Applies to finished (non-streaming) single-buffer responses larger than
* 1024 bytes with a compressible (text-like) `Content-Type`, preferring
* `br` over `gzip` per the client's `Accept-Encoding`. Responses with a
* pre-set `Content-Encoding` are never re-compressed. `Vary:
* accept-encoding` is set whenever compression was considered.
*
* Note: connection-global (read from the listener's root settings when
* the connection's handle is attached) - per-route values have no effect.
*/
uint8_t compress_dynamic;
/**
* Opt-in: enable permessage-deflate for WebSocket connections.
*
* Negotiation always forces both `*_no_context_takeover` flags (RFC 7692
* allows either endpoint to request them unilaterally), keeping
* persistent per-connection compression state at ~0.
*
* Note: connection-global (read from the listener's root settings when
* the connection's handle is attached) - per-route values have no effect.
*/
uint8_t compress_ws;
}
```
_Symbol type:_ `type`
#### `fio_http_listener_s`
```c
struct fio_http_listener_s
```
_Symbol type:_ `type`
#### `fio_http_resource_action_e`
```c
typedef enum {
FIO_HTTP_RESOURCE_NONE,
FIO_HTTP_RESOURCE_INDEX,
FIO_HTTP_RESOURCE_SHOW,
FIO_HTTP_RESOURCE_NEW,
FIO_HTTP_RESOURCE_EDIT,
FIO_HTTP_RESOURCE_CREATE,
FIO_HTTP_RESOURCE_UPDATE,
FIO_HTTP_RESOURCE_DELETE,
FIO_HTTP_RESOURCE_QUERY,
} fio_http_resource_action_e
```
_Symbol type:_ `type`
#### `fio_http_cookie_same_site_e`
```c
enum fio_http_cookie_same_site_e {
/** allow the browser to dictate this property */
FIO_HTTP_COOKIE_SAME_SITE_BROWSER_DEFAULT = 0,
/** The browser sends the cookie with cross-site and same-site requests. */
FIO_HTTP_COOKIE_SAME_SITE_NONE,
/**
* The cookie is withheld on cross-site sub-requests.
*
* The cookie is sent when a user navigates to the URL from an external
* site.
*/
FIO_HTTP_COOKIE_SAME_SITE_LAX,
/** The browser sends the cookie only for same-site requests. */
FIO_HTTP_COOKIE_SAME_SITE_STRICT,
}
```
Possible values for the `same_site` property in the cookie settings.
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
_Symbol type:_ `type`
#### `fio_http_cookie_args_s`
```c
struct fio_http_cookie_args_s {
/** The cookie's name. */
fio_str_info_s name;
/** The cookie's value (leave blank to delete cookie). */
fio_str_info_s value;
/** The cookie's domain (optional). */
fio_str_info_s domain;
/** The cookie's path (optional). */
fio_str_info_s path;
/** Max Age (how long should the cookie persist), in seconds (0 == session).*/
int max_age;
/** SameSite value. */
fio_http_cookie_same_site_e same_site;
/** Limit cookie to secure connections.*/
unsigned secure : 1;
/** Limit cookie to HTTP (intended to prevent JavaScript access/hijacking).*/
unsigned http_only : 1;
/**
* Set the Partitioned (third party) cookie flag:
* https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies
*/
unsigned partitioned : 1;
}
```
This is a helper for setting cookie data.
This struct is used together with the `fio_http_cookie_set` macro. i.e.:
fio_http_set_cookie(h,
.name = FIO_STR_INFO1("my_cookie"),
.value = FIO_STR_INFO1("data"));
_Symbol type:_ `type`
#### `fio_http_write_args_s`
```c
struct fio_http_write_args_s {
/** The data to be written. */
const void *buf;
/** The length of the data to be written. */
size_t len;
/** The offset at which writing should begin. */
size_t offset;
/** If streaming a file, set this value. The file is always closed. */
int fd;
/** If the data is a buffer, this callback may be set to free it once sent. */
void (*dealloc)(void *);
/** If the data is a buffer / a file - should it be copied? */
int copy;
/**
* If `finish` is set, this data marks the end of the response.
*
* Otherwise the response will stream the data.
*/
int finish;
}
```
Arguments for the fio_http_write function.
_Symbol type:_ `type`
#### `fio_http_body_parse_callbacks_s`
```c
typedef struct {
/* ===== Primitives ===== */
/** NULL / nil was detected. Returns new object. */
void *(*on_null)(void *udata);
/** TRUE was detected. Returns new object. */
void *(*on_true)(void *udata);
/** FALSE was detected. Returns new object. */
void *(*on_false)(void *udata);
/** Number was detected. Returns new object. */
void *(*on_number)(void *udata, int64_t num);
/** Float was detected. Returns new object. */
void *(*on_float)(void *udata, double num);
/** String was detected. Returns new object. */
void *(*on_string)(void *udata, const void *data, size_t len);
/* ===== Containers ===== */
/** Array was detected. Returns context for this array. */
void *(*on_array)(void *udata, void *parent);
/** Map / Object was detected. Returns context for this map. */
void *(*on_map)(void *udata, void *parent);
/** Push value to array. Returns non-zero on error. */
int (*array_push)(void *udata, void *array, void *value);
/** Set key-value pair in map. Returns non-zero on error. */
int (*map_set)(void *udata, void *map, void *key, void *value);
/** Called when array parsing is complete. */
void (*array_done)(void *udata, void *array);
/** Called when map parsing is complete. */
void (*map_done)(void *udata, void *map);
/* ===== File Uploads (multipart) ===== */
/**
* Called when a file upload starts.
*
* Return context for this file (e.g., fd, stream, buffer).
* Return NULL to skip this file (on_file_data/on_file_done won't be called).
*/
void *(*on_file)(void *udata,
fio_str_info_s name,
fio_str_info_s filename,
fio_str_info_s content_type);
/** Called for each chunk of file data. Return non-zero to abort. */
int (*on_file_data)(void *udata, void *file, fio_buf_info_s data);
/** Called when file upload is complete. */
void (*on_file_done)(void *udata, void *file);
/* ===== Error Handling ===== */
/** Called on parse error. `partial` is the incomplete result, if any. */
void *(*on_error)(void *udata, void *partial);
/** Called to free an unused object (e.g., key when map_set fails). */
void (*free_unused)(void *udata, void *obj);
} fio_http_body_parse_callbacks_s
```
HTTP body parser callbacks.
All callbacks receive `udata` as first parameter.
Primitive callbacks return the created object as `void *`.
Container callbacks return a context for that container.
_Symbol type:_ `type`
#### `fio_http_body_parse_result_s`
```c
typedef struct {
/** Top-level parsed object (caller responsible for freeing). */
void *result;
/** Number of bytes consumed from body. */
size_t consumed;
/** Error code: 0 = success. */
int err;
} fio_http_body_parse_result_s
```
HTTP body parse result.
_Symbol type:_ `type`
#### `fio_http_header_each_s`
```c
struct fio_http_header_each_s {
fio_buf_info_s name; /* header name, or property name */
fio_buf_info_s value; /* list value, or property value */
fio_buf_info_s properties; /* property span; cursor in property iterator */
unsigned index; /* index of the current repeated header field */
unsigned is_request;
}
```
A zero-copy cursor over repeated header fields, comma-separated values and
semicolon-separated properties. `name` / `value` are length-delimited slices
borrowed from the HTTP handle, NOT necessarily NUL-terminated. Quotes and
escapes are retained, not decoded. Do not mutate headers during iteration.
`properties` holds the bounded property span (or remaining properties).
`index` and `is_request` are iterator state; do not change them.
`value.buf == NULL` marks the end; a property may have a non-NULL value
with zero length. Empty list elements and empty property names are skipped.
_Symbol type:_ `type`
#### `fio_http_sse_write_args_s`
```c
typedef struct {
/** The message's `id` data (if any). */
fio_buf_info_s id;
/** The message's `event` data (if any). */
fio_buf_info_s event;
/** The message's `data` data (if any). */
fio_buf_info_s data;
} fio_http_sse_write_args_s
```
Named arguments for fio_http_sse_write.
_Symbol type:_ `type`
### Functions
#### `fio_http_io`
```c
fio_io_s *fio_http_io(fio_http_s *)
```
Returns the IO object associated with the HTTP object (request only).
_Symbol type:_ `function`
#### `fio_http_subscribe`
```c
#define fio_http_subscribe(h, ...) \
fio_pubsub_subscribe(.io = fio_http_io(h), __VA_ARGS__)
```
Macro helper for HTTP handle pub/sub subscriptions.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_listen`
```c
fio_http_listener_s *fio_http_listen(const char *url, fio_http_settings_s settings)
```
Listens to HTTP / WebSockets / SSE connections on `url`.
_Symbol type:_ `function`
#### `fio_http_listen`
```c
#define fio_http_listen(url, ...) \
fio_http_listen(url, (fio_http_settings_s){__VA_ARGS__})
```
Listens to HTTP / WebSockets / SSE connections on `url`.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_listener_settings`
```c
fio_http_settings_s *fio_http_listener_settings(fio_http_listener_s *l)
```
Returns the a pointer to the HTTP settings associated with the listener.
_Symbol type:_ `function`
#### `fio_http_route`
```c
int fio_http_route(fio_http_listener_s *listener, const char *url, fio_http_settings_s settings)
```
Adds a route prefix to the HTTP handler.
Order of route settings is irrelevant (unless overwriting an existing route).
Matching is performed as a best-prefix match. i.e.:
- All paths match the route `"/"` (the default prefix).
- The route `"/user"` will match `"/user"` and all `"/user/..."` paths but
not `"/user..."`
- Setting `"/user/new"` as well as `"/user"` (in whatever order) will route
`"/user/new"` and `"/user/new/..."` to `"/user/new"`. Otherwise, the
`"/user"` route will continue to behave the same.
Note: the following properties are inherited (if missing) from the
default HTTP settings used to create the listener: `udata`, `on_finish`,
`on_stop`, `on_authenticate_sse`, `on_authenticate_websocket`,
`max_header_size`, `max_line_len`, `max_body_size`, `ws_max_msg_size`,
`timeout`, `ws_timeout`, `sse_timeout`, `log`, `compress_static` and
`public_folder`.
Note: TLS options are ignored.
Note: only `on_http`, `on_finish`, `udata`, the authentication
callbacks, `public_folder`, `max_age` and `compress_static` are
effective per route. All other per-route values are stored but unused -
upgraded connection callbacks (`on_open`, `on_message`, etc.), `queue`,
`log`, limits, timeouts, `compress_dynamic` and `compress_ws` are
connection-global and are read from the listener's root settings.
_Symbol type:_ `function`
#### `fio_http_route`
```c
#define fio_http_route(listener, url, ...) \
fio_http_route(listener, url, (fio_http_settings_s){__VA_ARGS__})
```
Adds a route prefix to the HTTP handler.
Order of route settings is irrelevant (unless overwriting an existing route).
Matching is performed as a best-prefix match. i.e.:
- All paths match the route `"/"` (the default prefix).
- The route `"/user"` will match `"/user"` and all `"/user/..."` paths but
not `"/user..."`
- Setting `"/user/new"` as well as `"/user"` (in whatever order) will route
`"/user/new"` and `"/user/new/..."` to `"/user/new"`. Otherwise, the
`"/user"` route will continue to behave the same.
Note: the following properties are inherited (if missing) from the
default HTTP settings used to create the listener: `udata`, `on_finish`,
`on_stop`, `on_authenticate_sse`, `on_authenticate_websocket`,
`max_header_size`, `max_line_len`, `max_body_size`, `ws_max_msg_size`,
`timeout`, `ws_timeout`, `sse_timeout`, `log`, `compress_static` and
`public_folder`.
Note: TLS options are ignored.
Note: only `on_http`, `on_finish`, `udata`, the authentication
callbacks, `public_folder`, `max_age` and `compress_static` are
effective per route. All other per-route values are stored but unused -
upgraded connection callbacks (`on_open`, `on_message`, etc.), `queue`,
`log`, limits, timeouts, `compress_dynamic` and `compress_ws` are
connection-global and are read from the listener's root settings.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_route_settings`
```c
fio_http_settings_s *fio_http_route_settings(fio_http_listener_s *l, const char *url)
```
Returns a link to the settings matching `url`, as set by `fio_http_route`
_Symbol type:_ `function`
#### `fio_http_resource_action`
```c
inline fio_http_resource_action_e fio_http_resource_action(fio_http_s *h)
```
returns expected action or `FIO_HTTP_RESOURCE_NONE` on error.
_Symbol type:_ `function`
#### `fio_http_connect`
```c
fio_io_s *fio_http_connect(const char *url, fio_http_s *h, fio_http_settings_s settings)
```
Connects to HTTP / WebSockets / SSE connections on `url`.
_Symbol type:_ `function`
#### `fio_http_connect`
```c
#define fio_http_connect(url, h, ...) \
fio_http_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to HTTP / WebSockets / SSE connections on `url`.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_websocket_connect`
```c
fio_io_s *fio_http_websocket_connect(const char *url, fio_http_s *h, fio_http_settings_s settings)
```
Connects to a WebSocket server on `url`.
A convenience wrapper around `fio_http_connect` that ensures a `ws://` or
`wss://` scheme is used in the URL (`http://` becomes `ws://`, `https://`
becomes `wss://`, a missing scheme defaults to `ws://`).
The WebSocket upgrade request / response is handled automatically by the
underlying `fio_http_connect`: on acceptance (101) the connection
switches to the WebSocket callbacks (`on_open` / `on_message` /
`on_close`); on rejection the response is routed to `settings.on_http`.
_Symbol type:_ `function`
#### `fio_http_websocket_connect`
```c
#define fio_http_websocket_connect(url, h, ...) \
fio_http_websocket_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to a WebSocket server on `url` (see
`fio_http_websocket_connect`).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_new`
```c
fio_http_s *fio_http_new(void)
```
Create a new fio_http_s handle.
_Symbol type:_ `function`
#### `fio_http_new_copy_request`
```c
fio_http_s *fio_http_new_copy_request(fio_http_s *old)
```
Creates a copy of an existing handle, copying only its request data.
_Symbol type:_ `function`
#### `fio_http_free`
```c
void fio_http_free(fio_http_s *)
```
Reduces an fio_http_s handle's reference count or frees it.
_Symbol type:_ `function`
#### `fio_http_dup`
```c
fio_http_s *fio_http_dup(fio_http_s *)
```
Increases an fio_http_s handle's reference count.
_Symbol type:_ `function`
#### `fio_http_destroy`
```c
fio_http_s *fio_http_destroy(fio_http_s *h)
```
Destroyed the HTTP handle object, freeing all allocated resources.
_Symbol type:_ `function`
#### `fio_http_start_time_set`
```c
void fio_http_start_time_set(fio_http_s *)
```
Collects an updated timestamp for logging purposes.
_Symbol type:_ `function`
#### `fio_http_clear_response`
```c
fio_http_s *fio_http_clear_response(fio_http_s *h, bool clear_body)
```
Clears any response data.
_Symbol type:_ `function`
#### `fio_http_udata`
```c
inline void *fio_http_udata(fio_http_s *)
```
Gets the opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata_set`
```c
inline void *fio_http_udata_set(fio_http_s *, void *)
```
Sets the opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata2`
```c
inline void *fio_http_udata2(fio_http_s *)
```
Gets the second opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata2_set`
```c
inline void *fio_http_udata2_set(fio_http_s *, void *)
```
Sets a second opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_controller`
```c
inline fio_http_controller_s *fio_http_controller(fio_http_s *h)
```
Gets the HTTP Controller associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_controller_set`
```c
inline fio_http_controller_s *fio_http_controller_set( fio_http_s *h, fio_http_controller_s *controller)
```
Gets the HTTP Controller associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_cdata`
```c
inline void *fio_http_cdata(fio_http_s *h)
```
Returns the existing controller data (`void *` pointer).
_Symbol type:_ `function`
#### `fio_http_cdata_set`
```c
inline void *fio_http_cdata_set(fio_http_s *h, void *cdata)
```
Sets a new controller data (`void *` pointer).
_Symbol type:_ `function`
#### `fio_http_status`
```c
size_t fio_http_status(fio_http_s *)
```
Gets the status associated with the HTTP handle (response).
_Symbol type:_ `function`
#### `fio_http_status_set`
```c
size_t fio_http_status_set(fio_http_s *, size_t status)
```
Sets the status associated with the HTTP handle (response).
_Symbol type:_ `function`
#### `fio_http_method`
```c
fio_str_info_s fio_http_method(fio_http_s *)
```
Gets the method information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_method_set`
```c
fio_str_info_s fio_http_method_set(fio_http_s *, fio_str_info_s)
```
Sets the method information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_opath`
```c
fio_str_info_s fio_http_opath(fio_http_s *)
```
Gets the original / first path associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_opath_set`
```c
fio_str_info_s fio_http_opath_set(fio_http_s *, fio_str_info_s)
```
Sets the original / first path associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_path`
```c
fio_str_info_s fio_http_path(fio_http_s *)
```
Gets the path information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_path_set`
```c
fio_str_info_s fio_http_path_set(fio_http_s *, fio_str_info_s)
```
Sets the path information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_query`
```c
fio_str_info_s fio_http_query(fio_http_s *)
```
Gets the query information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_query_set`
```c
fio_str_info_s fio_http_query_set(fio_http_s *, fio_str_info_s)
```
Sets the query information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_version`
```c
fio_str_info_s fio_http_version(fio_http_s *)
```
Gets the version information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_version_set`
```c
fio_str_info_s fio_http_version_set(fio_http_s *, fio_str_info_s)
```
Sets the version information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_received_at`
```c
int64_t fio_http_received_at(fio_http_s *)
```
Gets the received_at timestamp (ms) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_received_at_set`
```c
int64_t fio_http_received_at_set(fio_http_s *, int64_t)
```
Sets the received_at timestamp (ms) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header`
```c
fio_str_info_s fio_http_request_header(fio_http_s *, fio_str_info_s name, size_t index)
```
Gets the header information associated with the HTTP handle.
Since more than a single value may be associated with a header name, the
index may be used to collect subsequent values.
An empty value is returned if no header value is available (or index is
exceeded).
_Symbol type:_ `function`
#### `fio_http_request_header_count`
```c
size_t fio_http_request_header_count(fio_http_s *, fio_str_info_s name)
```
Returns the number of headers named `name` that were received.
If `name` buffer is `NULL`, returns the number of unique headers (not the
number of unique values).
_Symbol type:_ `function`
#### `fio_http_request_header_set`
```c
fio_str_info_s fio_http_request_header_set(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_set_if_missing`
```c
fio_str_info_s fio_http_request_header_set_if_missing(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_add`
```c
fio_str_info_s fio_http_request_header_add(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Adds to the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_each`
```c
size_t fio_http_request_header_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all request headers (except cookies!).
A non-zero return will stop iteration.
Returns the number of iterations performed. If `callback` is `NULL`, returns
the number of headers available (multi-value headers are counted as 1).
_Symbol type:_ `function`
#### `fio_http_body_length`
```c
size_t fio_http_body_length(fio_http_s *)
```
Gets the body (payload) length associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_body_seek`
```c
size_t fio_http_body_seek(fio_http_s *, ssize_t pos)
```
Adjusts the body's reading position. Negative values start at the end.
If `pos == SSIZE_MAX`, returns `fio_http_body_pos`.
_Symbol type:_ `function`
#### `fio_http_body_pos`
```c
size_t fio_http_body_pos(fio_http_s *h)
```
Returns the body's reading position.
_Symbol type:_ `function`
#### `fio_http_body_read`
```c
fio_str_info_s fio_http_body_read(fio_http_s *, size_t length)
```
Reads up to `length` of data from the body, returns nothing on EOF.
_Symbol type:_ `function`
#### `fio_http_body_read_until`
```c
fio_str_info_s fio_http_body_read_until(fio_http_s *, char token, size_t limit)
```
Reads from the body until finding `token`, reaching `limit` or EOF.
Note: `limit` is ignored if zero or if the remaining data is lower than
limit.
_Symbol type:_ `function`
#### `fio_http_body_expect`
```c
void fio_http_body_expect(fio_http_s *, size_t expected_length)
```
Allocates a body (payload) of (at least) the `expected_length`.
_Symbol type:_ `function`
#### `fio_http_body_write`
```c
void fio_http_body_write(fio_http_s *, const void *data, size_t len)
```
Writes `data` to the body (payload) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_body_fd`
```c
int fio_http_body_fd(fio_http_s *)
```
If the body is stored in a temporary file, returns the file's handle.
Otherwise returns -1.
_Symbol type:_ `function`
#### `fio_http_body_close`
```c
void fio_http_body_close(fio_http_s *)
```
Releases body (payload) resources, closing any temporary files.
_Symbol type:_ `function`
#### `fio_http_cookie_set`
```c
int fio_http_cookie_set(fio_http_s *h, fio_http_cookie_args_s)
```
Sets a response cookie.
Returns -1 on error and 0 on success.
Note: Long cookie names and long cookie values will be considered a security
violation and an error will be returned. Many browsers and proxies impose
limits on headers and cookies, cookies often limited to 4Kb in total for both
name and value.
_Symbol type:_ `function`
#### `fio_http_cookie_set`
```c
#define fio_http_cookie_set(http___handle, ...) \
fio_http_cookie_set((http___handle), (fio_http_cookie_args_s){__VA_ARGS__})
```
Named arguments helper. See fio_http_cookie_args_s for details.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_cookie`
```c
fio_str_info_s fio_http_cookie(fio_http_s *, const char *name, size_t name_len)
```
Returns a cookie value (either received of newly set), if any.
_Symbol type:_ `function`
#### `fio_http_cookie_each`
```c
size_t fio_http_cookie_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all cookies. A non-zero return will stop iteration.
_Symbol type:_ `function`
#### `fio_http_set_cookie_each`
```c
size_t fio_http_set_cookie_each(fio_http_s *h, int (*callback)(fio_http_s *, fio_str_info_s set_cookie_header, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all response set cookies.
A non-zero return value from the callback will stop iteration.
_Symbol type:_ `function`
#### `fio_http_is_clean`
```c
int fio_http_is_clean(fio_http_s *)
```
Returns true if no HTTP headers / data was sent (a clean slate).
_Symbol type:_ `function`
#### `fio_http_is_finished`
```c
int fio_http_is_finished(fio_http_s *)
```
Returns true if the HTTP handle's response was sent.
_Symbol type:_ `function`
#### `fio_http_is_streaming`
```c
int fio_http_is_streaming(fio_http_s *)
```
Returns true if the HTTP handle's response is streaming.
_Symbol type:_ `function`
#### `fio_http_is_upgraded`
```c
int fio_http_is_upgraded(fio_http_s *h)
```
Returns true if the HTTP connection was (or should have been) upgraded.
_Symbol type:_ `function`
#### `fio_http_is_websocket`
```c
int fio_http_is_websocket(fio_http_s *)
```
Returns true if the HTTP handle refers to a WebSocket connection.
_Symbol type:_ `function`
#### `fio_http_is_sse`
```c
int fio_http_is_sse(fio_http_s *)
```
Returns true if the HTTP handle refers to an EventSource connection.
_Symbol type:_ `function`
#### `fio_http_is_freeing`
```c
int fio_http_is_freeing(fio_http_s *)
```
Returns true if handle is in the process of freeing itself.
_Symbol type:_ `function`
#### `fio_http_response_header`
```c
fio_str_info_s fio_http_response_header(fio_http_s *, fio_str_info_s name, size_t index)
```
Gets the header information associated with the HTTP handle.
Since more than a single value may be associated with a header name, the
index may be used to collect subsequent values.
An empty value is returned if no header value is available (or index is
exceeded).
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_count`
```c
size_t fio_http_response_header_count(fio_http_s *, fio_str_info_s name)
```
Returns the number of headers named `name` in the response.
If `name` buffer is `NULL`, returns the number of unique headers (not the
number of unique values).
_Symbol type:_ `function`
#### `fio_http_response_header_set`
```c
fio_str_info_s fio_http_response_header_set(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_set_if_missing`
```c
fio_str_info_s fio_http_response_header_set_if_missing(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_add`
```c
fio_str_info_s fio_http_response_header_add(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Adds to the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_each`
```c
size_t fio_http_response_header_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all response headers (except cookies!).
A non-zero return will stop iteration.
_Symbol type:_ `function`
#### `fio_http_write`
```c
void fio_http_write(fio_http_s *, fio_http_write_args_s args)
```
Writes `data` to the response body associated with the HTTP handle after
sending all headers (no further headers may be sent).
_Symbol type:_ `function`
#### `fio_http_write`
```c
#define fio_http_write(http_handle, ...) \
fio_http_write(http_handle, (fio_http_write_args_s){__VA_ARGS__})
```
Named arguments helper. See fio_http_write and fio_http_write_args_s.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_finish`
```c
#define fio_http_finish(http_handle) fio_http_write(http_handle, .finish = 1)
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_close`
```c
void fio_http_close(fio_http_s *h)
```
Closes the associated connection after previously scheduled output drains.
Schedule the complete response before calling this (i.e., in on_finish).
_Symbol type:_ `function`
#### `fio_http_mimetype_register`
```c
int fio_http_mimetype_register(char *file_ext, size_t file_ext_len, fio_str_info_s mime_type)
```
Registers a Mime-Type to be associated with the file extension.
_Symbol type:_ `function`
#### `fio_http_mimetype`
```c
fio_str_info_s fio_http_mimetype(char *file_ext, size_t file_ext_len)
```
Finds the Mime-Type associated with the file extension (if registered).
_Symbol type:_ `function`
#### `fio_http_body_parse`
```c
fio_http_body_parse_result_s fio_http_body_parse(fio_http_s *h, const fio_http_body_parse_callbacks_s *callbacks, void *udata)
```
Parses the HTTP request body, auto-detecting content type.
Supports JSON, URL-encoded, and multipart/form-data bodies.
Calls the appropriate callbacks for each element found.
**Parameters:**
- `h` - The HTTP handle.
- `callbacks` - Parser callbacks (designed to be static const).
- `udata` - User context passed to all callbacks.
**Returns:**
- Parse result with top-level object and status.
_Symbol type:_ `function`
#### `fio_http_header_each_first`
```c
inline fio_http_header_each_s fio_http_header_each_first(fio_http_s *http_handle, int is_request, fio_str_info_s header_name)
```
Starts iterating a header's values. `header_name` is a borrowed lookup key.
Returns an iterator with `value.buf == NULL` when no value is available.
_Symbol type:_ `function`
#### `fio_http_header_each_next`
```c
inline void fio_http_header_each_next(fio_http_header_each_s *pos, fio_http_s *http_handle)
```
Advances to the next value; `http_handle` must be the original handle.
Sets `value.buf` to NULL after the last value.
_Symbol type:_ `function`
#### `fio_http_header_each_property_first`
```c
inline fio_http_header_each_s fio_http_header_each_property_first(const fio_buf_info_s *properties)
```
Starts iterating a value's bounded property span. `properties` is borrowed.
Returns an iterator with `value.buf == NULL` when no property exists.
_Symbol type:_ `function`
#### `fio_http_header_each_property_next`
```c
inline void fio_http_header_each_property_next(fio_http_header_each_s *pos)
```
Advances to the next property. A property without `=` has an empty value.
Sets `value.buf` to NULL after the last property.
_Symbol type:_ `function`
#### `fio_http_send_error_response`
```c
int fio_http_send_error_response(fio_http_s *h, size_t status)
```
Sends the requested error message and finishes the response.
_Symbol type:_ `function`
#### `fio_http_etag_is_match`
```c
int fio_http_etag_is_match(fio_http_s *h)
```
Returns true (1) if the ETag response matches an if-none-match request.
_Symbol type:_ `function`
#### `fio_http_static_file_response`
```c
int fio_http_static_file_response(fio_http_s *h, fio_str_info_s root_folder, fio_str_info_s file_name, size_t max_age)
```
Attempts to send a static file from the `root_folder` folder.
`file_name` is URL-decoded and appended to `root_folder` (path traversal
is rejected). Folders resolve to their `index` file and missing
extensions are auto-completed (`.html`, `.htm`, `.txt`, `.md` -
`FIO_HTTP_STATIC_FILE_COMPLETION`). `OPTIONS` requests are refused (a
static file is not a valid `OPTIONS` response).
`root_folder` MUST name an existing folder (use `"."` for the CWD):
the path-traversal guard rejects `..` folding, not absolute paths, so
an empty root would expose absolute paths such as `/etc/passwd`.
Settings-provided `public_folder` values are validated on listen/route
creation (non-existent folders are rejected); direct callers MUST pass
a valid, non-empty root. Symlinks inside the root are followed (like
nginx's default) - applications requiring strict containment should
keep the root free of symlinks.
Handles conditional requests (`ETag` / `If-None-Match` -> 304), single
`Range` requests (206 / 416, always served identity), and `HEAD`
requests; sets `Last-Modified`, `Accept-Ranges: bytes`, and (when
`max_age` is non-zero) `Cache-Control: max-age=...`.
Pre-compressed variants (`file.br`, `file.zstd`, `file.gz`, `file.zip`,
in this preference order) are served when accepted by the client,
present on disk, and fresh; with `compress_static` enabled (the matching
route's settings, or the `FIO_HTTP_CFLAG_COMPRESS_STATIC` cflag on
detached handles), missing `.br` / `.gz` variants are also created on
demand.
On success the response is complete and 0 is returned. On failure -1 is
returned and the application should handle the request itself.
_Symbol type:_ `function`
#### `fio_http_status2str`
```c
fio_str_info_s fio_http_status2str(size_t status)
```
Returns a human readable string related to the HTTP status number.
_Symbol type:_ `function`
#### `fio_http_write_log`
```c
void fio_http_write_log(fio_http_s *h)
```
Logs an HTTP (response) to STDOUT.
_Symbol type:_ `function`
#### `fio_http_from`
```c
int fio_http_from(fio_str_info_s *dest, const fio_http_s *h)
```
Writes peer address to `dest` starting with the `forwarded` header, with a
fallback to actual socket address and a final fallback to `"[unknown]"`.
If `unknown` is returned, the function returns -1. if `dest` capacity is too
small, the number of bytes required will be returned.
If all goes well, this function returns 0.
_Symbol type:_ `function`
#### `fio_http_date`
```c
fio_str_info_s fio_http_date(uint64_t now_in_seconds)
```
_Symbol type:_ `function`
#### `fio_http_log_time`
```c
fio_str_info_s fio_http_log_time(uint64_t now_in_seconds)
```
_Symbol type:_ `function`
#### `fio_http_websocket_requested`
```c
int fio_http_websocket_requested(fio_http_s *)
```
Returns non-zero if request headers ask for a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_accepted`
```c
int fio_http_websocket_accepted(fio_http_s *h)
```
Returns non-zero if the response accepts a WebSocket upgrade request.
_Symbol type:_ `function`
#### `fio_http_upgrade_websocket`
```c
void fio_http_upgrade_websocket(fio_http_s *)
```
Sets response data to agree to a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_set_request`
```c
void fio_http_websocket_set_request(fio_http_s *)
```
Sets request data to request a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_requested`
```c
int fio_http_sse_requested(fio_http_s *)
```
Returns non-zero if request headers ask for an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_accepted`
```c
int fio_http_sse_accepted(fio_http_s *h)
```
Returns non-zero if the response accepts an SSE request.
_Symbol type:_ `function`
#### `fio_http_upgrade_sse`
```c
void fio_http_upgrade_sse(fio_http_s *)
```
Sets response data to agree to an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_set_request`
```c
void fio_http_sse_set_request(fio_http_s *)
```
Sets request data to request an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_write`
```c
int fio_http_websocket_write(fio_http_s *h, const void *buf, size_t len, uint8_t is_text)
```
Writes a WebSocket message. Fails if connection wasn't upgraded yet.
_Symbol type:_ `function`
#### `fio_http_on_message_set`
```c
int fio_http_on_message_set(fio_http_s *h, void (*on_message)(fio_http_s *, fio_buf_info_s, uint8_t))
```
Sets a specific on_message callback for this connection.
Returns -1 on error (i.e., upgrade still in negotiation).
_Symbol type:_ `function`
#### `fio_http_sse_write`
```c
int fio_http_sse_write(fio_http_s *h, fio_http_sse_write_args_s args)
```
Writes an SSE message (UTF-8). Fails if connection wasn't upgraded yet.
_Symbol type:_ `function`
#### `fio_http_sse_write`
```c
#define fio_http_sse_write(h, ...) \
fio_http_sse_write((h), ((fio_http_sse_write_args_s){__VA_ARGS__}))
```
Writes an SSE message (UTF-8). Fails if connection wasn't upgraded yet.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_settings`
```c
fio_http_settings_s *fio_http_settings(fio_http_s *)
```
Returns the HTTP settings associated with the HTTP object, if any.
_Symbol type:_ `function`
-----------------------------------------------------
# facil.io C STL edge — complete documentation
# facil.io C STL edge Documentation
The edge build of the 0.8.x line re-organizes facil.io as the **fio-stl** library — a single-header-style C STL plus IO, HTTP, Pub/Sub and TLS 1.3 modules. Source code: [facil-io/cstl](https://github.com/facil-io/cstl).
**[Download fio-stl.h](https://github.com/facil-io/cstl/releases/download/edge/fio-stl.h)** — the entire library as a single header file (edge build, always current, one click).
> Edge build — rolling fio-stl development docs, synced from the cstl edge tag.
The documentation is organized into module guides below, plus a complete [API Reference](https://facil.io/edge/api/) generated from the headers. Press / or ⌘K anywhere to search.
## Core Library
Version macros, compiler shims, atomics, memory primitives, SIMD and math — the foundation everything builds on.
- [facil.io STL — Core](https://facil.io/edge/facil-io-stl-core/)
- [Atomics and Locks](https://facil.io/edge/atomics-and-locks/)
- [Bit Operations](https://facil.io/edge/bit-operations/)
- [Compiler Attributes](https://facil.io/edge/compiler-attributes/)
- [Constant-Time Helpers](https://facil.io/edge/constant-time-helpers/)
- [Endian and Byte Ordering](https://facil.io/edge/endian-and-byte-ordering/)
- [Linked Lists](https://facil.io/edge/linked-lists/)
- [Logging and Assertions](https://facil.io/edge/logging-and-assertions/)
- [Core Math Helpers](https://facil.io/edge/core-math-helpers/)
- [Memory Alternatives and OS Patches](https://facil.io/edge/memory-alternatives-and-os-patches/)
- [Memory Primitives](https://facil.io/edge/memory-primitives/)
- [OS Detection](https://facil.io/edge/os-detection/)
- [Core Random PRNG](https://facil.io/edge/core-random-prng/)
- [Fake x86 SIMD](https://facil.io/edge/fake-x86-simd/)
- [Static Scratch Allocator](https://facil.io/edge/static-scratch-allocator/)
- [String and Buffer Information](https://facil.io/edge/string-and-buffer-information/)
- [UTF-8 Helpers](https://facil.io/edge/utf-8-helpers/)
- [Vector Math](https://facil.io/edge/vector-math/)
- [Version and Settings](https://facil.io/edge/version-and-settings/)
## Utilities & Parsers
Text conversion, hashing, glob, sorting, threads and the wire-format parsers (HTTP/1.x, JSON, RESP3, WebSocket…).
- [String / Number Conversion](https://facil.io/edge/string-number-conversion/)
- [CRC32](https://facil.io/edge/crc32/)
- [Glob Matching](https://facil.io/edge/glob-matching/)
- [Index Mapped Array (iMap)](https://facil.io/edge/index-mapped-array-imap/)
- [Multi-Precision Math](https://facil.io/edge/multi-precision-math/)
- [Pseudo-Random and Hashing](https://facil.io/edge/pseudo-random-and-hashing/)
- [Signal Monitoring](https://facil.io/edge/signal-monitoring/)
- [Sorting](https://facil.io/edge/sorting/)
- [Portable Threads](https://facil.io/edge/portable-threads/)
- [URL / URI Parsing](https://facil.io/edge/url-uri-parsing/)
- [ML Entity Decoding](https://facil.io/edge/ml-entity-decoding/)
- [File Helpers](https://facil.io/edge/file-helpers/)
- [HTTP/1.x Parser](https://facil.io/edge/http-1-x-parser/)
- [JSON Parser](https://facil.io/edge/json-parser/)
- [MIME Multipart Parser](https://facil.io/edge/mime-multipart-parser/)
- [RESP3 Parser](https://facil.io/edge/resp3-parser/)
- [Basic Socket Helpers](https://facil.io/edge/basic-socket-helpers/)
- [State Callbacks](https://facil.io/edge/state-callbacks/)
- [Time Helpers](https://facil.io/edge/time-helpers/)
- [URL-Encoded Parser](https://facil.io/edge/url-encoded-parser/)
- [WebSocket Parser](https://facil.io/edge/websocket-parser/)
- [CLI Helpers](https://facil.io/edge/cli-helpers/)
- [Memory Allocator](https://facil.io/edge/memory-allocator/)
- [String Core](https://facil.io/edge/string-core/)
- [GFM Markdown Parser](https://facil.io/edge/gfm-markdown-parser/)
## System, Streams & Templates
Polling backends, task and timer queues, packet streams, Markdown and Mustache rendering.
- [POSIX Portable Polling API](https://facil.io/edge/posix-portable-polling-api/)
- [POSIX Polling — epoll Backend](https://facil.io/edge/posix-polling-epoll-backend/)
- [POSIX Polling — kqueue Backend](https://facil.io/edge/posix-polling-kqueue-backend/)
- [POSIX Polling — poll Backend](https://facil.io/edge/posix-polling-poll-backend/)
- [Task and Timer Queues](https://facil.io/edge/task-and-timer-queues/)
- [Packet Data Stream](https://facil.io/edge/packet-data-stream/)
- [Markdown to HTML bstr Renderer](https://facil.io/edge/markdown-to-html-bstr-renderer/)
- [Mustache-ish Template Engine](https://facil.io/edge/mustache-ish-template-engine/)
## Cryptography & TLS
Hashes, AEAD, signatures, X.509, KDFs, password hashing, compression and a standalone TLS 1.3 stack.
- [Crypto Core](https://facil.io/edge/crypto-core/)
- [BLAKE2](https://facil.io/edge/blake2/)
- [ChaCha20 & Poly1305](https://facil.io/edge/chacha20-poly1305/)
- [HKDF](https://facil.io/edge/hkdf/)
- [SHA-1](https://facil.io/edge/sha-1/)
- [SHA-2](https://facil.io/edge/sha-2/)
- [SHA-3](https://facil.io/edge/sha-3/)
- [AES-GCM](https://facil.io/edge/aes-gcm/)
- [Ed25519 & X25519](https://facil.io/edge/ed25519-x25519/)
- [P-256 (secp256r1)](https://facil.io/edge/p-256-secp256r1/)
- [P-384 (secp384r1)](https://facil.io/edge/p-384-secp384r1/)
- [ASN.1 DER](https://facil.io/edge/asn-1-der/)
- [RSA Signatures](https://facil.io/edge/rsa-signatures/)
- [ML-KEM-768](https://facil.io/edge/ml-kem-768/)
- [X.509 Certificates](https://facil.io/edge/x-509-certificates/)
- [PEM Parser](https://facil.io/edge/pem-parser/)
- [Argon2](https://facil.io/edge/argon2/)
- [Lyra2](https://facil.io/edge/lyra2/)
- [OTP (TOTP)](https://facil.io/edge/otp-totp/)
- [Secrets](https://facil.io/edge/secrets/)
- [Brotli](https://facil.io/edge/brotli/)
- [DEFLATE / Gzip](https://facil.io/edge/deflate-gzip/)
- [TLS 1.3 Standalone Library](https://facil.io/edge/tls-1-3-standalone-library/)
## Dynamic Types
Dynamic strings, arrays, hash maps, reference counting and the FIOBJ soft dynamic types.
- [Dynamic Types — Module Group 200](https://facil.io/edge/dynamic-types-module-group-200/)
- [Dynamic Strings — Module 201](https://facil.io/edge/dynamic-strings-module-201/)
- [Dynamic Arrays — Module 202](https://facil.io/edge/dynamic-arrays-module-202/)
- [Hash Maps and Sets — Module 210](https://facil.io/edge/hash-maps-and-sets-module-210/)
- [Reference Counting Wrapper — Module 249](https://facil.io/edge/reference-counting-wrapper-module-249/)
- [FIOBJ — Soft Dynamic Types — Module 250](https://facil.io/edge/fiobj-soft-dynamic-types-module-250/)
## IO, HTTP & Pub/Sub
The IO reactor, TLS backends, Pub/Sub, Redis and the HTTP module that ties it all together.
- [IO and HTTP — Group Overview (400-range)](https://facil.io/edge/io-and-http-group-overview-400-range/)
- [IO Reactor API (401 io api.h)](https://facil.io/edge/io-reactor-api-401-io-api-h/)
- [IPC — Inter-Process Communication (404 ipc.h)](https://facil.io/edge/ipc-inter-process-communication-404-ipc-h/)
- [OpenSSL TLS Backend](https://facil.io/edge/openssl-tls-backend/)
- [Native TLS 1.3 IO Backend](https://facil.io/edge/native-tls-1-3-io-backend/)
- [Pub/Sub — Publish/Subscribe Messaging (420 pubsub.h)](https://facil.io/edge/pub-sub-publish-subscribe-messaging-420-pubsub-h/)
- [Redis — Pub/Sub Engine and Command Client (422 redis.h)](https://facil.io/edge/redis-pub-sub-engine-and-command-client-422-redis-h/)
- [HTTP Module](https://facil.io/edge/http-module/)
## API Reference
Every macro, type and function, per header file:
- [Core](https://facil.io/edge/api/#grp-core)
- [Utilities](https://facil.io/edge/api/#grp-util)
- [System](https://facil.io/edge/api/#grp-system)
- [Cryptography](https://facil.io/edge/api/#grp-crypto)
- [Dynamic Types](https://facil.io/edge/api/#grp-types)
- [IO & HTTP](https://facil.io/edge/api/#grp-io)
# fio-stl API Reference
The complete macro, type and function reference, one page per header file. Counts are listed per header.
## Core (000–1)
- [`000 copyright.h`](https://facil.io/edge/api/000-copyright/) — 1 macro
- [`000 core.h`](https://facil.io/edge/api/000-core/) — 311 macros, 14 types, 1439 functions
- [`001 header.h`](https://facil.io/edge/api/001-header/) — 12 macros
- [`001 logging.h`](https://facil.io/edge/api/001-logging/) — 1 macro
- [`001 memalt.h`](https://facil.io/edge/api/001-memalt/) — 5 functions
- [`001 patches.h`](https://facil.io/edge/api/001-patches/) — 2 functions
## Utilities (002–12)
- [`002 atol.h`](https://facil.io/edge/api/002-atol/) — 4 macros, 1 type, 44 functions
- [`002 crc32.h`](https://facil.io/edge/api/002-crc32/) — 1 function
- [`002 glob matching.h`](https://facil.io/edge/api/002-glob-matching/) — 1 function
- [`002 imap.h`](https://facil.io/edge/api/002-imap/) — 7 macros
- [`002 math.h`](https://facil.io/edge/api/002-math/) — 6 functions
- [`002 random.h`](https://facil.io/edge/api/002-random/) — 1 macro, 14 functions
- [`002 signals.h`](https://facil.io/edge/api/002-signals/) — 3 macros, 1 type, 4 functions
- [`002 sort.h`](https://facil.io/edge/api/002-sort/) — 3 macros
- [`002 threads.h`](https://facil.io/edge/api/002-threads/) — 2 macros, 5 types, 26 functions
- [`002 url.h`](https://facil.io/edge/api/002-url/) — 1 macro, 3 types, 3 functions
- [`003 entities.h`](https://facil.io/edge/api/003-entities/) — 1 function
- [`004 files.h`](https://facil.io/edge/api/004-files/) — 4 macros, 3 types, 21 functions
- [`004 http1 parser.h`](https://facil.io/edge/api/004-http1-parser/) — 3 macros
- [`004 json.h`](https://facil.io/edge/api/004-json/) — 2 macros, 2 types, 1 function
- [`004 multipart.h`](https://facil.io/edge/api/004-multipart/) — 2 types, 1 function
- [`004 resp3.h`](https://facil.io/edge/api/004-resp3/) — 19 macros, 4 types, 1 function
- [`004 sock.h`](https://facil.io/edge/api/004-sock/) — 9 macros, 2 types, 24 functions
- [`004 state callbacks.h`](https://facil.io/edge/api/004-state-callbacks/) — 1 type, 3 functions
- [`004 time.h`](https://facil.io/edge/api/004-time/) — 17 functions
- [`004 urlencoded.h`](https://facil.io/edge/api/004-urlencoded/) — 2 types, 1 function
- [`004 websocket parser.h`](https://facil.io/edge/api/004-websocket-parser/) — 29 macros
- [`005 cli.h`](https://facil.io/edge/api/005-cli/) — 7 macros, 1 type, 15 functions
- [`010 mem.h`](https://facil.io/edge/api/010-mem/) — 27 macros, 23 functions
- [`011 string core.h`](https://facil.io/edge/api/011-string-core/) — 18 macros, 3 types, 82 functions
- [`012 gfm.h`](https://facil.io/edge/api/012-gfm/) — 10 macros
## System (102–104)
- [`102 poll api.h`](https://facil.io/edge/api/102-poll-api/) — 7 macros, 2 types, 7 functions
- [`102 queue.h`](https://facil.io/edge/api/102-queue/) — 3 macros, 4 types, 21 functions
- [`102 stream.h`](https://facil.io/edge/api/102-stream/) — 3 macros, 2 types, 11 functions
- [`103 md2html.h`](https://facil.io/edge/api/103-md2html/) — 2 macros
- [`104 mustache.h`](https://facil.io/edge/api/104-mustache/) — 5 macros, 3 types, 6 functions
## Cryptography (150–190)
- [`150 crypto core.h`](https://facil.io/edge/api/150-crypto-core/) — 2 types
- [`152 blake2.h`](https://facil.io/edge/api/152-blake2/) — 2 types, 12 functions
- [`152 chacha20poly1305.h`](https://facil.io/edge/api/152-chacha20poly1305/) — 7 functions
- [`152 sha1.h`](https://facil.io/edge/api/152-sha1/) — 1 type, 3 functions
- [`152 sha2.h`](https://facil.io/edge/api/152-sha2/) — 2 types, 12 functions
- [`152 sha2z hkdf.h`](https://facil.io/edge/api/152-sha2z-hkdf/) — 2 macros, 3 functions
- [`152 sha3.h`](https://facil.io/edge/api/152-sha3/) — 1 type, 16 functions
- [`153 aes.h`](https://facil.io/edge/api/153-aes/) — 4 functions
- [`154 ed25519.h`](https://facil.io/edge/api/154-ed25519/) — 2 macros, 11 functions
- [`154 p256.h`](https://facil.io/edge/api/154-p256/) — 5 functions
- [`154 p384.h`](https://facil.io/edge/api/154-p384/) — 2 functions
- [`155 der.h`](https://facil.io/edge/api/155-der/) — 4 types, 14 functions
- [`155 rsa.h`](https://facil.io/edge/api/155-rsa/) — 3 macros, 3 types, 3 functions
- [`156 mlkem.h`](https://facil.io/edge/api/156-mlkem/) — 9 macros, 8 functions
- [`156 x509.h`](https://facil.io/edge/api/156-x509/) — 34 macros, 10 types, 13 functions
- [`157 pem.h`](https://facil.io/edge/api/157-pem/) — 3 types, 5 functions
- [`159 argon2.h`](https://facil.io/edge/api/159-argon2/) — 2 types, 4 functions
- [`159 lyra2.h`](https://facil.io/edge/api/159-lyra2/) — 1 type, 4 functions
- [`159 otp.h`](https://facil.io/edge/api/159-otp/) — 1 type, 6 functions
- [`159 secret.h`](https://facil.io/edge/api/159-secret/) — 5 functions
- [`162 brotli.h`](https://facil.io/edge/api/162-brotli/) — 4 functions
- [`162 deflate.h`](https://facil.io/edge/api/162-deflate/) — 1 type, 12 functions
- [`190 tls13.h`](https://facil.io/edge/api/190-tls13/) — 16 macros, 16 types, 28 functions
## Dynamic Types (200–250)
- [`201 string.h`](https://facil.io/edge/api/201-string/) — 7 macros, 1 type, 39 functions
- [`202 array.h`](https://facil.io/edge/api/202-array/) — 9 macros, 2 types, 19 functions
- [`210 map.h`](https://facil.io/edge/api/210-map/) — 3 macros, 4 types, 23 functions
- [`210 map2.h`](https://facil.io/edge/api/210-map2/) — 3 macros
- [`249 reference counter.h`](https://facil.io/edge/api/249-reference-counter/) — 17 functions
- [`250 fiobj.h`](https://facil.io/edge/api/250-fiobj/) — 24 macros, 2 types, 40 functions
## IO & HTTP (400–439)
- [`401 io api.h`](https://facil.io/edge/api/401-io-api/) — 6 macros, 13 types, 85 functions
- [`404 ipc.h`](https://facil.io/edge/api/404-ipc/) — 13 macros, 4 types, 24 functions
- [`405 tls13.h`](https://facil.io/edge/api/405-tls13/) — 1 function
- [`420 pubsub.h`](https://facil.io/edge/api/420-pubsub/) — 2 macros, 4 types, 20 functions
- [`422 redis.h`](https://facil.io/edge/api/422-redis/) — 2 macros, 2 types, 6 functions
- [`430 http api.h`](https://facil.io/edge/api/430-http-api/) — 34 macros, 12 types, 107 functions
# facil.io STL — Core
## The C Server Toolbox Library
Welcome to the heart of the facil.io C STL. This is where the boring-but-essential stuff lives: version numbers, compiler incantations, OS patches, atomics, memory primitives, and the low-level helpers that everything else builds on. It is not glamorous, but without it the rest of the library would be a pile of undefined behavior and segfaults.
The core group covers the first two slices of the library — `000 core.h` and `001 header.h` — plus their specialized sidekicks. Each file below is a focused guide to one corner of that foundation.
## Core documentation index
- [001 version and settings](https://facil.io/edge/version-and-settings/) — Version macros and the compile-time knobs that tune the library's behavior.
- [001 compiler attributes](https://facil.io/edge/compiler-attributes/) — Compiler detection, C/C++ attribute helpers, and pointer-math macros.
- [001 os detection](https://facil.io/edge/os-detection/) — POSIX/Windows detection, process helpers, and portable type patches.
- [001 atomics and locks](https://facil.io/edge/atomics-and-locks/) — Atomic operations, spinlocks, atomic bitmap access, and thread-yield hints.
- [001 static alloc](https://facil.io/edge/static-scratch-allocator/) — The round-robin scratch allocator for short-lived temporary buffers.
- [001 logging](https://facil.io/edge/logging-and-assertions/) — Log levels, assertion macros, and the leak counter.
- [001 endian](https://facil.io/edge/endian-and-byte-ordering/) — Byte-order detection, swaps, and conversions between host and network/little-endian layouts.
- [001 memory primitives](https://facil.io/edge/memory-primitives/) — Low-level copy/move/compare helpers and unaligned buffer accessors.
- [001 memalt](https://facil.io/edge/memory-alternatives-and-os-patches/) — Portable fallback memory routines and the environment helpers they lean on.
- [001 vector math](https://facil.io/edge/vector-math/) — Wide integer unions from 128 to 4096 bits and their lane-wise arithmetic.
- [001 simd](https://facil.io/edge/fake-x86-simd/) — Fake/portable x86 intrinsics and SIMD helpers built on the wide vector types.
- [001 linked lists](https://facil.io/edge/linked-lists/) — Pointer-based and indexed linked-list macros.
- [001 constant time](https://facil.io/edge/constant-time-helpers/) — Branchless selection, bitwise comparisons, and other security-sensitive primitives.
- [001 bit operations](https://facil.io/edge/bit-operations/) — Rotations, popcount, bitmap access, and byte-value bit tricks.
- [001 math](https://facil.io/edge/core-math-helpers/) — Hash primes, modular arithmetic, and multi-precision add/sub/mul helpers.
- [001 random core](https://facil.io/edge/core-random-prng/) — The deterministic 128-bit PRNG and its reseeding/cycle-counter hooks.
- [001 string info](https://facil.io/edge/string-and-buffer-information/) — Lightweight string and buffer descriptors plus equality helpers.
- [001 utf8](https://facil.io/edge/utf-8-helpers/) — Encoding, decoding, and validation helpers for UTF-8 text.
Everything after the core builds on these pages. Pick a topic and dig in.
# Atomics and Locks
Concurrency primitives live in [`./000 core.h`](https://facil.io/edge/api/000-core/). The lock selector that chooses between spinlocks and OS mutexes is in [`./001 header.h`](https://facil.io/edge/api/001-header/).
## Atomic operations
All atomic macros use sequentially consistent ordering. They work on a pointer to the object and return the **previous** value, unless the name ends in `_fetch`, in which case they return the **new** value.
### Load and exchange
| Macro | Returns | Notes |
|---|---|---|
| `fio_atomic_load(dest, p_obj)` | assigns value to `dest` | Atomic read of `*p_obj`. |
| `fio_atomic_exchange(p_obj, value)` | previous value | Atomically sets `*p_obj = value`. |
| `fio_atomic_compare_exchange_p(p_obj, p_expected, p_desired)` | `1` on success, `0` on failure | System-specific compare-and-swap. `p_expected` may be overwritten with the current value. |
### Arithmetic and logic
| Macro | Operation | Returns |
|---|---|---|
| `fio_atomic_add(p_obj, value)` | `*p_obj += value` | previous value |
| `fio_atomic_sub(p_obj, value)` | `*p_obj -= value` | previous value |
| `fio_atomic_and(p_obj, value)` | `*p_obj &= value` | previous value |
| `fio_atomic_or(p_obj, value)` | `*p_obj \|= value` | previous value |
| `fio_atomic_xor(p_obj, value)` | `*p_obj ^= value` | previous value |
| `fio_atomic_add_fetch(p_obj, value)` | `*p_obj += value` | new value |
| `fio_atomic_sub_fetch(p_obj, value)` | `*p_obj -= value` | new value |
| `fio_atomic_and_fetch(p_obj, value)` | `*p_obj &= value` | new value |
| `fio_atomic_or_fetch(p_obj, value)` | `*p_obj \|= value` | new value |
| `fio_atomic_xor_fetch(p_obj, value)` | `*p_obj ^= value` | new value |
The implementation prefers GCC/Clang `__atomic` builtins, falls back to legacy `__sync` builtins, then C11 `stdatomic.h`, then MSVC intrinsics.
## Spinlocks
The default lock is an 8-bit spinlock with one main lock and seven sub-locks packed into the same byte.
| Type / macro | Meaning |
|---|---|
| `fio_lock_i` | `volatile unsigned char` spinlock type. |
| `FIO_LOCK_INIT` | Initializer (`0`). |
| `FIO_LOCK_SUBLOCK(i)` | Bit mask for sub-lock `i` (`0` to `7`). Combine with `\|`. |
### Single lock API
| Function | Behavior |
|---|---|
| `uint8_t fio_trylock(fio_lock_i *lock)` | Try to acquire the main lock. Returns `0` on success, `1` on failure. |
| `void fio_lock(fio_lock_i *lock)` | Busy-wait until the main lock is acquired. Avoid long waits. |
| `void fio_unlock(fio_lock_i *lock)` | Release the main lock. |
| `uint8_t fio_is_locked(fio_lock_i *lock)` | Returns non-zero if the main lock is held. |
### Group lock API
| Function | Behavior |
|---|---|
| `uint8_t fio_trylock_group(fio_lock_i *lock, uint8_t group)` | Try to acquire a group of sub-locks. Returns `0` on success, `1` on failure. |
| `void fio_lock_group(fio_lock_i *lock, uint8_t group)` | Busy-wait until the whole group is acquired. |
| `void fio_unlock_group(fio_lock_i *lock, uint8_t group)` | Release a group of sub-locks. |
| `uint8_t fio_is_group_locked(fio_lock_i *lock, uint8_t group)` | Returns non-zero if any sub-lock in `group` is held. |
| `uint8_t fio_trylock_full(fio_lock_i *lock)` | Try to lock all sub-locks at once. |
| `void fio_lock_full(fio_lock_i *lock)` | Busy-wait until every sub-lock is acquired. |
| `void fio_unlock_full(fio_lock_i *lock)` | Release every sub-lock. |
Example:
```c
fio_lock_i lock = FIO_LOCK_INIT;
if (!fio_trylock_group(&lock,
FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2))) {
/* critical section */
fio_unlock_group(&lock, FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2));
}
```
## Atomic bitmap access
Operate on a single bit inside a byte array. All four helpers are atomic at the byte level.
| Function | Behavior |
|---|---|
| `uint8_t fio_atomic_bit_get(void *map, size_t bit)` | Read bit `bit` from `map`. |
| `void fio_atomic_bit_set(void *map, size_t bit)` | Set bit `bit` to `1`. |
| `void fio_atomic_bit_unset(void *map, size_t bit)` | Set bit `bit` to `0`. |
| `void fio_atomic_bit_flip(void *map, size_t bit)` | Toggle bit `bit`. |
## Lock selector
[`./001 header.h`](https://facil.io/edge/api/001-header/) chooses between facil.io spinlocks and OS mutexes. The default is spinlocks unless `FIO_USE_THREAD_MUTEX` is set to `1`.
| Macro | Purpose |
|---|---|
| `FIO_USE_THREAD_MUTEX_TMP` | Per-include override. Defaults to `FIO_USE_THREAD_MUTEX`. |
| `FIO_THREADS` | Defined when OS mutexes are selected. |
| `FIO___LOCK_NAME` | Human-readable name of the selected lock type. |
| `FIO___LOCK_TYPE` | The selected lock type (`fio_thread_mutex_t` or `fio_lock_i`). |
| `FIO___LOCK_INIT` | Initializer for the selected lock type. |
| `FIO___LOCK_DESTROY(lock)` | Destroy a lock instance. |
| `FIO___LOCK_LOCK(lock)` | Acquire the lock. |
| `FIO___LOCK_TRYLOCK(lock)` | Try to acquire the lock. |
| `FIO___LOCK_UNLOCK(lock)` | Release the lock. |
These macros are used by modules that need thread safety but do not care which lock type is active.
## Thread scheduling hints
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/) and used by the spinlock back-off loops.
| Macro | Behavior |
|---|---|
| `FIO_THREAD_WAIT(nano_sec)` | Sleep the current thread for about `nano_sec` nanoseconds. POSIX uses `nanosleep`; Windows uses `Sleep` (rounded up to at least 1 ms). |
| `FIO_THREAD_YIELD()` | Hint to the CPU that the thread is spinning. Emits `pause` on x86, `yield` on ARM, calls `YieldProcessor()` on MSVC, or falls back to `sched_yield()` on POSIX. |
| `FIO_THREAD_RESCHEDULE()` | Short sleep via `FIO_THREAD_WAIT(4)`. Used by `fio_lock_group` to back off without fully suspending the thread. |
# Bit Operations
Bit-twiddling helpers defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
## Bit rotation
### Generic rotation macros
```c
#define FIO_LROT(i, bits)
#define FIO_RROT(i, bits)
```
Rotate the value `i` left or right by `bits`. The rotation count is masked to the width of `i`, so these macros work for any integer type.
### Typed rotation helpers
Each width has a left and right variant. When the compiler provides a rotate builtin it is used; otherwise a portable shift-or expression is emitted. All are marked `FIO_CONST`.
Note: the `fio_rrot8` builtin guard has a typo (`__has_builtin(__builtin_rotatrightt8)`), so the 8-bit right rotation always falls back to the portable implementation.
| Width | Left | Right |
|---|---|---|
| 8-bit | `fio_lrot8(i, bits)` | `fio_rrot8(i, bits)` |
| 16-bit | `fio_lrot16(i, bits)` | `fio_rrot16(i, bits)` |
| 32-bit | `fio_lrot32(i, bits)` | `fio_rrot32(i, bits)` |
| 64-bit | `fio_lrot64(i, bits)` | `fio_rrot64(i, bits)` |
| 128-bit | `fio_lrot128(i, bits)` | `fio_rrot128(i, bits)` |
The 128-bit variants are only available when the compiler supports `__int128`/`__uint128_t`.
### Endian-aware forward rotation
```c
fio_frot16(i, bits)
fio_frot32(i, bits)
fio_frot64(i, bits)
```
On little-endian systems `fio_frot*` maps to the right-rotation helpers; on big-endian systems it maps to the left-rotation helpers. These are macros, not functions.
## Combined rotation-XOR operations
Common SHA-2/BLAKE2-style combinations kept as separate operations so the compiler can optimize them as a unit.
```c
uint32_t fio_xor_rrot3_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c);
uint64_t fio_xor_rrot3_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c);
```
Computes `ROTR(x,a) ^ ROTR(x,b) ^ ROTR(x,c)`. Used by SHA-256/SHA-512 Sigma functions.
```c
uint32_t fio_xor_rrot2_shr_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c);
uint64_t fio_xor_rrot2_shr_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c);
```
Computes `ROTR(x,a) ^ ROTR(x,b) ^ (x >> c)`. Used by SHA-256/SHA-512 lowercase sigma functions for message scheduling.
## XOR masking
```c
void fio_xmask(void *buf, size_t len, uint64_t mask);
void fio_xmask_cpy(char *restrict dest,
const char *src,
size_t len,
uint64_t mask);
```
XOR every byte of a buffer with a repeating 64-bit `mask`. `fio_xmask` works in place; `fio_xmask_cpy` reads from `src` and writes to `dest`. If `dest == src`, `fio_xmask_cpy` behaves like `fio_xmask`.
Both functions process data in 64-bit chunks for speed and fall back to a byte-sized tail for the last `0`–`7` bytes. Performance is best when the buffer is aligned.
## Popcount and Hamming distance
```c
int fio_popcount(uint64_t n);
```
Returns the number of `1` bits in `n`. Uses `__builtin_popcountll` or MSVC `__popcnt64` when available; otherwise falls back to a portable parallel bit-count implementation.
```c
#define fio_hemming_dist(n1, n2) \
fio_popcount(((uint64_t)(n1) ^ (uint64_t)(n2)))
```
Returns the Hamming distance between `n1` and `n2` — the number of bits that differ. The macro is named `fio_hemming_dist` in the source (the conventional spelling is "Hamming").
## Bit isolation and indexing
```c
uint64_t fio_bits_lsb(uint64_t i);
uint64_t fio_bits_msb(uint64_t i);
```
* `fio_bits_lsb(i)` isolates the least-significant set bit.
* `fio_bits_msb(i)` isolates the most-significant set bit.
```c
size_t fio_bits_lsb_index(uint64_t i);
size_t fio_bits_msb_index(uint64_t i);
```
Return the bit index of the least-significant or most-significant set bit. Both return `(size_t)-1` when `i` is `0`.
```c
size_t fio_lsb_index_unsafe(uint64_t i);
size_t fio_msb_index_unsafe(uint64_t i);
```
Same as above but assume `i != 0`; calling them with `0` is undefined behavior. They use `__builtin_ctzll`/`__builtin_clzll` when available and fall back to `fio___single_bit_index_unsafe` — a `switch` over 64 power-of-two constants — otherwise.
## Byte detection in vectors
These helpers detect special byte values inside a packed integer. In the result, a matching byte lane is set to `0x80`; all other lanes are `0x00`.
### 32-bit vectors
```c
uint32_t fio_has_zero_byte32(uint32_t row);
uint32_t fio_has_byte32(uint32_t row, uint8_t byte);
uint32_t fio_has_full_byte32(uint32_t row);
```
* `fio_has_zero_byte32` detects `0x00` bytes.
* `fio_has_byte32` detects an arbitrary byte value.
* `fio_has_full_byte32` detects `0x00` bytes — the source's inline comment claims `0xFF`, but the implementation calls `fio_has_zero_byte32(row)`.
### 64-bit vectors
```c
uint64_t fio_has_zero_byte64(uint64_t row);
uint64_t fio_has_zero_byte_alt64(uint64_t row);
uint64_t fio_has_byte64(uint64_t row, uint8_t byte);
uint64_t fio_has_full_byte64(uint64_t row);
uint64_t fio_has_byte2bitmap(uint64_t result);
```
* `fio_has_zero_byte64` detects `0x00` bytes.
* `fio_has_zero_byte_alt64` is an alternate entry point with the same behavior as `fio_has_zero_byte64`; the source's warning about bitmap safety may be stale.
* `fio_has_byte64` detects an arbitrary byte value.
* `fio_has_full_byte64` detects `0xFF` bytes.
* `fio_has_byte2bitmap` packs a `fio_has_byte*` result into a single 8-bit bitmap.
## Bitmap access
Operate on a single bit inside a byte array. These are **not** atomic; use the [`fio_atomic_bit_*`](https://facil.io/edge/atomics-and-locks/#atomic-bitmap-access) helpers for shared mutable bitmaps.
```c
uint8_t fio_bit_get(void *map, size_t bit);
void fio_bit_set(void *map, size_t bit);
void fio_bit_unset(void *map, size_t bit);
void fio_bit_flip(void *map, size_t bit);
```
* `fio_bit_get` returns `0` or `1`.
* `fio_bit_set` sets the bit to `1`.
* `fio_bit_unset` sets the bit to `0`.
* `fio_bit_flip` toggles the bit.
## See also
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth for all macros and functions above.
- [`./001 atomics and locks.md`](https://facil.io/edge/atomics-and-locks/) — atomic bitmap helpers and locks.
- [`./001 constant time.md`](https://facil.io/edge/constant-time-helpers/) — branchless bitwise primitives.
- [`./001 endian.md`](https://facil.io/edge/endian-and-byte-ordering/) — byte ordering helpers.
# Compiler Attributes
The macros in [`./000 core.h`](https://facil.io/edge/api/000-core/) and [`./001 header.h`](https://facil.io/edge/api/001-header/) paper over differences between C and C++, compilers, and inclusion models. This page is a quick reference for that portability scaffolding.
## C++ compatibility
Both core slices open an `extern "C"` block when compiled as C++. [`./000 core.h`](https://facil.io/edge/api/000-core/) also neutralizes C-only keywords inside that block:
- `register` is defined away (deprecated in C++).
- `restrict` is defined away (not valid in C++).
- `_Bool` is mapped to `bool`.
No action is required; including the headers gives C++ code a C-compatible surface.
## Keyword shims
Some compilers need a little help with C99/C11 keywords:
- **`inline`** — mapped to `__inline` when the compiler does not support the C99 keyword directly (mainly older MSVC).
- **`__thread`** — mapped to `__declspec(thread)` on MSVC and `_Thread_local` on C11 compilers; otherwise left as `__thread` when the compiler supports it.
## Compiler detection and no-op macros
When the compiler is not GCC or Clang, `__attribute__`, `__has_include`, `__has_builtin`, and `__has_attribute` are defined as no-ops so the rest of the code can use them unconditionally.
- **`FIO_NOOP`** — empty macro that adds whitespace. Useful to stop a function-like macro from being expanded.
- **`FIO_NOOP_FN(...)`** — empty variadic macro, handy as a default callback.
- **`FIO_NOOP_FN_NAME`** — expands to `(void)`.
## OS detection
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/); see [`./001 os detection.md`](https://facil.io/edge/os-detection/) for full details.
- **`FIO_OS_POSIX`** — 1 on POSIX systems.
- **`FIO_OS_WIN`** — 1 on Windows.
- **`FIO_HAVE_UNIX_TOOLS`** — 0 (none), 1 (POSIX), 2 (MinGW), or 3 (Cygwin).
## Function attributes
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/) and used throughout the library.
- **`FIO_MAYBE_UNUSED`** — `__attribute__((unused))` on GCC/Clang, empty otherwise.
- **`FIO_SFUNC`** — `static FIO_MAYBE_UNUSED`. Default internal linkage.
- **`FIO_IFUNC`** — `FIO_SFUNC inline`. Static inline helper.
- **`FIO_WARN_UNUSED`** — `__attribute__((warn_unused_result))` or empty.
- **`FIO_MIFN`** — `FIO_IFUNC FIO_WARN_UNUSED`. Inline math-style function that warns if the return value is ignored.
- **`FIO_CONST`** — function result depends only on arguments; no globals or side effects.
- **`FIO_PURE`** — no side effects, but may read global memory.
- **`FIO_WEAK`** — weak symbol. Empty on MinGW.
- **`DEPRECATED(reason)`** — `__attribute__((deprecated(reason)))`, with a fallback for GCC older than 4.5.
- **`FIO_CONSTRUCTOR(fname)`** — runs before `main` on GCC/Clang; on MSVC it places a pointer in `.CRT$XCU`.
- **`FIO_DESTRUCTOR(fname)`** — runs after `main` on GCC/Clang; on MSVC it is implemented as a constructor that registers `atexit(fname)`.
- **`FIO_LIKELY(cond)`** / **`FIO_UNLIKELY(cond)`** — branch-prediction hints. No-op on unknown compilers.
- **`FIO_PREFETCH(ptr)`** / **`FIO_PREFETCH_W(ptr)`** / **`FIO_PREFETCH_NT(ptr)`** — cache prefetch hints for read, write-exclusive, and non-temporal streaming access.
- **`FIO___PRINTF_STYLE(string_index, check_index)`** — tells the compiler to check `printf`-style format arguments. Empty on pure MSVC; uses the MinGW/Cygwin format attribute on those compilers; otherwise uses the GCC/Clang `printf` format attribute.
## Variable and type attributes
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
- **`FIO_ALIGN(bytes)`** — alignment hint. Uses `__attribute__((aligned(bytes)))` on GCC/Clang; currently empty on other compilers.
- **`FIO_WEAK_VAR`** — weak global variable. Uses `__declspec(selectany)` on Windows when selectany support is detected (MSVC or otherwise); `__attribute__((weak))` elsewhere.
## Macro stringifier
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
- **`FIO_MACRO2STR(macro)`** — expands `macro` and stringifies the result. Uses a two-step indirection so tokens like `__LINE__` are expanded first.
## Static assertions
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
- **`FIO_ASSERT_STATIC(cond, msg)`** — compile-time assertion. Uses C11 `_Static_assert` when available; otherwise falls back to a negative-array-size trick that produces a compile error on failure.
## Naming helpers
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
- **`FIO_NAME(prefix, postfix)`** → `prefix_postfix`.
- **`FIO_NAME2(prefix, postfix)`** → `prefix2postfix` (for conversion functions).
- **`FIO_NAME_BL(prefix, postfix)`** → `prefix_is_postfix` (for boolean tests).
- **`FIO_NAME_TEST(prefix, postfix)`** → internal test-function name (`fio___test_prefix_postfix`).
## Pointer math
All defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
- **`FIO_PTR_MATH_LMASK(T, ptr, bits)`** — keep the low `bits` of an address.
- **`FIO_PTR_MATH_RMASK(T, ptr, bits)`** — keep the high bits, zero the low `bits`.
- **`FIO_PTR_MATH_ADD(T, ptr, offset)`** — add `offset` bytes and cast to `T *`.
- **`FIO_PTR_MATH_SUB(T, ptr, offset)`** — subtract `offset` bytes and cast to `T *`.
- **`FIO_PTR_FIELD_OFFSET(T, field)`** — byte offset of `field` inside `T`. Uses `0xFF00` as a base address to keep AddressSanitizer happy.
- **`FIO_PTR_FROM_FIELD(T, field, ptr)`** — recover the containing `T` object from a pointer to one of its fields.
## Pointer tagging
Defined in [`./001 header.h`](https://facil.io/edge/api/001-header/); see [`./001 version and settings.md`](https://facil.io/edge/version-and-settings/) for full details.
- **`FIO_PTR_TAG(p)`** / **`FIO_PTR_UNTAG(p)`** — default no-ops; override to embed tags in pointers.
- **`FIO_PTR_TAG_TYPE`** — when defined, pointer-returning functions use this type instead.
- **`FIO_PTR_TAG_VALIDATE(ptr)`** — validates a tagged pointer; default accepts non-NULL.
- **`FIO_PTR_TAG_VALID_OR_RETURN(tagged_ptr, value)`** — return `value` if validation fails.
- **`FIO_PTR_TAG_VALID_OR_RETURN_VOID(tagged_ptr)`** — return void if validation fails.
- **`FIO_PTR_TAG_VALID_OR_GOTO(tagged_ptr, label)`** — jump to `label` if validation fails.
- **`FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr)`** — cast `FIO_PTR_UNTAG(tagged_ptr)` to `untagged_type *`.
## Get/set helpers
Generate typed getters and setters for struct fields.
- **`FIO_DEF_GETSET_FUNC(static, namespace, T_type, F_type, F_name, on_set)`** — defines both `namespace_F_name` (getter) and `namespace_F_name_set` (setter).
- **`FIO_DEF_GETSET_FUNC_DEC(static, namespace, T_type, F_type, F_name)`** — declarations only.
- **`FIO_IFUNC_DEF_GETSET(namespace, T_type, F_type, F_name, on_set)`** — same, but uses `FIO_IFUNC` as the linkage. `FIO_IFUNC_DEF_GET` and `FIO_IFUNC_DEF_SET` exist for the individual pieces.
The setter returns the old value and calls `on_set(o)` after assignment.
## Recursive inclusion and linkage
facil.io headers are designed to be included more than once to generate code. [`./001 header.h`](https://facil.io/edge/api/001-header/) manages whether generated functions are `static` or `extern`.
- **`SFUNC_`** / **`IFUNC_`** — internal linkage selectors. On the first include they become `FIO_SFUNC`/`FIO_IFUNC` unless `FIO_EXTERN` is defined, in which case they are empty.
- **`SFUNC`** / **`IFUNC`** — public aliases for the current linkage selectors.
- **`FIO_EXTERN`** — define before including a module to emit non-static declarations and inline functions.
- **`FIO___RECURSIVE_INCLUDE`** — internal flag. Setting it to `99` before a recursive `#include` preserves the original `SFUNC`/`IFUNC` linkage instead of forcing it back to `static`.
## Compiler memory barriers
- **`FIO_COMPILER_GUARD`** — clobber memory and prevent the compiler from reordering instructions around it.
- **`FIO_COMPILER_GUARD_INSTRUCTION`** — prevent instruction reordering without the full memory clobber.
On GCC/Clang these are inline `asm volatile` barriers; on MSVC they map to `_ReadWriteBarrier` and `_WriteBarrier`.
## Thread scheduling
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/); see [`./001 atomics and locks.md`](https://facil.io/edge/atomics-and-locks/) for full details.
- **`FIO_THREAD_WAIT(nano_sec)`** — sleep for about `nano_sec` nanoseconds (`Sleep` on Windows, `nanosleep` on POSIX).
- **`FIO_THREAD_YIELD()`** — yield the CPU (`pause`/`yield` instruction or `sched_yield`).
- **`FIO_THREAD_RESCHEDULE()`** — short sleep via `FIO_THREAD_WAIT(4)`.
## Address-sanitizer scaffolding
- **`FIO___ASAN_DETECTED`** — set when AddressSanitizer is active.
- **`FIO___ASAN_AVOID`** — expands to `no_sanitize_address` when ASan is detected; otherwise empty.
## Loop helpers
- **`FIO_FOR(i, count)`** — `for (size_t i = 0; i < (count); ++i)`.
- **`FIO_FOR_UNROLL(iterations, size_of_loop, i, action)`** — splits a loop into a small remainder and 256-byte chunks to nudge the compiler toward auto-vectorization. Used heavily by the memory primitives and vector-math templates. The SIMD file covers the portable x86 intrinsics that build on these helpers.
# Constant-Time Helpers
Branchless, timing-aware primitives defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
These helpers avoid data-dependent branches and early exits so that execution time does not leak information about the values being processed. They are used by the cryptographic modules, authentication code, and anywhere else a compiler might otherwise turn a secret into a branch.
## Boolean constants
```c
uintmax_t fio_ct_true(uintmax_t cond);
uintmax_t fio_ct_false(uintmax_t cond);
```
* `fio_ct_true(cond)` returns `1` if `cond` is non-zero, else `0`.
* `fio_ct_false(cond)` returns `1` if `cond` is zero, else `0`.
Both return only the value `0` or `1`, suitable for feeding into `fio_ct_if_bool`.
## Conditional selection
```c
uintmax_t fio_ct_if_bool(uintmax_t cond, uintmax_t a, uintmax_t b);
uintmax_t fio_ct_if(uintmax_t cond, uintmax_t a, uintmax_t b);
```
* `fio_ct_if_bool` returns `a` when `cond == 1`, otherwise `b`.
* `fio_ct_if` first normalizes `cond` with `fio_ct_true`, then returns `a` when `cond` is non-zero, otherwise `b`.
Both are branchless: they compute `b ^ (mask & (a ^ b))`.
## Min, max, and absolute value
```c
intmax_t fio_ct_max(intmax_t a, intmax_t b);
intmax_t fio_ct_min(intmax_t a, intmax_t b);
uintmax_t fio_ct_abs(intmax_t i);
```
* `fio_ct_max` / `fio_ct_min` perform signed comparisons without branches.
* `fio_ct_abs` returns the absolute value of `i`.
## ASCII case conversion
```c
char fio_ct_tolower(char c);
```
Returns the lowercase form of `c` when `c` is an ASCII uppercase letter (`A`–`Z`); otherwise returns `c` unchanged. Branchless and locale-independent.
## Bitwise mux, majority, and three-way XOR
```c
uint32_t fio_ct_mux32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_mux64(uint64_t x, uint64_t y, uint64_t z);
uint32_t fio_ct_maj32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_maj64(uint64_t x, uint64_t y, uint64_t z);
uint32_t fio_ct_xor3_32(uint32_t x, uint32_t y, uint32_t z);
uint64_t fio_ct_xor3_64(uint64_t x, uint64_t y, uint64_t z);
```
| Function | Operation | Formula |
|----------|-----------|---------|
| `fio_ct_mux32/64` | bitwise choose | `z ^ (x & (y ^ z))` |
| `fio_ct_maj32/64` | bitwise majority | `(x & y) \| (z & (x \| y))` |
| `fio_ct_xor3_32/64` | bitwise parity | `x ^ y ^ z` |
These are the SHA-style `Ch`, `Maj`, and parity functions, implemented without branches.
## Constant-time equality
```c
_Bool fio_ct_is_eq(const void *a, const void *b, size_t bytes);
```
Compares two memory regions and returns `1` if they are byte-for-byte identical, `0` otherwise. The comparison is timing-attack resistant: it always reads every byte and accumulates differences into a single flag before returning, regardless of where the first mismatch occurs.
## Secure zero and stack wipe
```c
void fio_secure_zero(void *a_, size_t bytes);
```
Zeros `bytes` starting at `a_`. The write is performed through a `volatile` pointer so the compiler cannot optimize it away. Use this for passwords, keys, and other sensitive material that must be erased from memory.
```c
#define FIO_MEM_STACK_WIPE(pages)
```
Allocates a volatile stack array of `(pages) * 4096` bytes and initializes it to zero. Useful for clearing sensitive stack scratch space; the `(void)stack_mem` use keeps the array alive through the scope.
# Endian and Byte Ordering
Macros and inline helpers for detecting and converting byte order. All are defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
facil.io assumes one of the two common orderings: big-endian or little-endian. Mixed-endian systems are not supported.
## Compile-Time Detection
The library normalizes two common compiler-provided symbols so downstream code can test endianness with simple `#if` checks.
- **`__BIG_ENDIAN__`** — set to `1` on big-endian systems, `0` on little-endian systems.
- **`__LITTLE_ENDIAN__`** — set to `1` on little-endian systems, `0` on big-endian systems.
The library also checks `__BYTE_ORDER__` as an input hint when the compiler provides it. If the compiler already defines `__BIG_ENDIAN__`, `__LITTLE_ENDIAN__`, or `__BYTE_ORDER__`, those values are honored. Otherwise facil.io defines `FIO_ENDIAN_ORDER_TEST` (`'1234'`), `FIO_LITTLE_ENDIAN_TEST` (`0x31323334UL`), and `FIO_BIG_ENDIAN_TEST` (`0x34333231UL`), then compares the unprefixed names in `#if ENDIAN_ORDER_TEST == LITTLE_ENDIAN_TEST` and `#elif ENDIAN_ORDER_TEST == BIG_ENDIAN_TEST`. If neither matches, compilation fails with an error.
The comparison deliberately uses the unprefixed names `ENDIAN_ORDER_TEST`, `LITTLE_ENDIAN_TEST`, and `BIG_ENDIAN_TEST`; those exact identifiers are not defined by facil.io. This mirrors the source.
These are constants, not variables; use them in `#if __BIG_ENDIAN__` rather than runtime `if` statements when possible.
## Runtime Detection
```c
unsigned int fio_is_little_endian(void);
unsigned int fio_is_big_endian(void);
```
Runtime tests that return a non-zero value when the machine matches the named ordering. `fio_is_big_endian` is implemented as `!fio_is_little_endian()`. Compilers usually constant-fold the result, but the functions are provided for cases where a runtime check is clearer.
## Byte Swapping
Reverse the byte order of a fixed-width integer. Each macro expands to the corresponding compiler builtin when available; otherwise it falls back to a portable bit-shifting implementation.
- `fio_bswap8(i)` — identity, defined so generic code can use the `8`-bit width without special cases.
- `fio_bswap16(i)` — swap bytes of a 16-bit integer.
- `fio_bswap32(i)` — swap bytes of a 32-bit integer.
- `fio_bswap64(i)` — swap bytes of a 64-bit integer.
- `fio_bswap128(i)` — swap bytes of a 128-bit integer. Only available when the compiler supports `__int128`/`__uint128_t`.
## Host ↔ Network Conversions
Network byte order is big-endian. These macros convert between the local byte order and network byte order.
- `fio_lton16(i)`, `fio_lton32(i)`, `fio_lton64(i)` — local to network.
- `fio_ntol16(i)`, `fio_ntol32(i)`, `fio_ntol64(i)` — network to local.
- `fio_lton128(i)` — local to network, defined only on little-endian systems where the compiler supports `__int128`/`__uint128_t`.
- `fio_ntol128(i)` — network to local, defined only when the compiler supports `__int128`/`__uint128_t`.
On big-endian systems the 16/32/64-bit macros are no-ops. On little-endian systems they byte-swap.
`fio_lton8(i)` and `fio_ntol8(i)` are also defined as identities so width-generic code can treat 8-bit values uniformly.
## Host ↔ Little-Endian Conversions
These helpers force values into little-endian byte order, which is also the ordering used by several internal data structures.
- `fio_ltole16(i)`, `fio_ltole32(i)`, `fio_ltole64(i)` — local to little-endian.
- `fio_ltole128(i)` — local to little-endian, only available when the compiler supports `__int128`/`__uint128_t`.
On little-endian systems they are no-ops. On big-endian systems they byte-swap.
`fio_ltole8(i)` is defined as an identity for generic width handling.
## Endian-Aware Shifts
A pair of shift macros whose direction depends on the host endianness. They are used in code that processes multi-byte values as bit patterns where the conceptual "forward" or "backward" direction reverses with endianness.
On little-endian systems:
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) << (bits))
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) >> (bits))
```
On big-endian systems they are defined only when the compiler supports `__int128`/`__uint128_t`:
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) >> (bits))
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) << (bits))
```
## See Also
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth for all macros and functions above.
- [`./000 core.md`](https://facil.io/edge/facil-io-stl-core/) — overview of core types and helpers.
# Linked Lists
```c
#include "fio-stl.h"
```
Small, sharp linked-list macros from [`./000 core.h`](https://facil.io/edge/api/000-core/). There are two families:
- pointer-based circular doubly linked lists, using `next` / `prev` pointers;
- indexed circular doubly linked lists, using integer offsets into an array.
The macros do not allocate memory, do not own stored objects, and do not check that nodes are initialized. Fast tools, pointy edges.
### Types
#### `fio_list_node_s`
```c
typedef struct fio_list_node_s {
struct fio_list_node_s *next;
struct fio_list_node_s *prev;
} fio_list_node_s;
```
The pointer-based list link. Embed it inside the struct you want to place in a list.
#### `FIO_LIST_NODE`
```c
#define FIO_LIST_NODE fio_list_node_s
```
Semantic alias for an embedded node field.
#### `FIO_LIST_HEAD`
```c
#define FIO_LIST_HEAD fio_list_node_s
```
Semantic alias for a list head/root. A list head is also a node, and an empty list points back to itself.
#### `fio_index32_node_s`
```c
typedef struct fio_index32_node_s {
uint32_t next;
uint32_t prev;
} fio_index32_node_s;
```
A 32-bit indexed-list link. It stores indexes into a caller-owned array.
#### `fio_index16_node_s`
```c
typedef struct fio_index16_node_s {
uint16_t next;
uint16_t prev;
} fio_index16_node_s;
```
A 16-bit indexed-list link.
#### `fio_index8_node_s`
```c
typedef struct fio_index8_node_s {
uint8_t next;
uint8_t prev;
} fio_index8_node_s;
```
An 8-bit indexed-list link.
#### `FIO_INDEXED_LIST32_NODE`, `FIO_INDEXED_LIST32_HEAD`
```c
#define FIO_INDEXED_LIST32_NODE fio_index32_node_s
#define FIO_INDEXED_LIST32_HEAD uint32_t
```
32-bit indexed-list node and head index types.
#### `FIO_INDEXED_LIST16_NODE`, `FIO_INDEXED_LIST16_HEAD`
```c
#define FIO_INDEXED_LIST16_NODE fio_index16_node_s
#define FIO_INDEXED_LIST16_HEAD uint16_t
```
16-bit indexed-list node and head index types.
#### `FIO_INDEXED_LIST8_NODE`, `FIO_INDEXED_LIST8_HEAD`
```c
#define FIO_INDEXED_LIST8_NODE fio_index8_node_s
#define FIO_INDEXED_LIST8_HEAD uint8_t
```
8-bit indexed-list node and head index types.
### API Functions
#### `FIO_LIST_INIT`
```c
#define FIO_LIST_INIT(obj) \
(fio_list_node_s) { .next = &(obj), .prev = &(obj) }
```
Initializer for a pointer-list node or head. The node points to itself.
Use it for heads:
```c
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
```
and for standalone nodes before insertion:
```c
item_s item = {.node = FIO_LIST_INIT(item.node)};
```
#### `FIO_LIST_EACH`
```c
#define FIO_LIST_EACH(type, node_name, head, pos) \
for (type *pos = FIO_PTR_FROM_FIELD(type, node_name, (head)->next), \
*next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, node_name, (head)->next->next); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.next)))
```
Iterates forward over every object after `head`. `pos` is declared by the macro as `type *`.
The next pointer is cached before the loop body, so removing the current `pos` is supported.
#### `FIO_LIST_EACH_REVERSED`
```c
#define FIO_LIST_EACH_REVERSED(type, node_name, head, pos) \
for (type *pos = FIO_PTR_FROM_FIELD(type, node_name, (head)->prev), \
*next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, node_name, (head)->next->prev); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.prev)))
```
Attempts to iterate backward over every object before `head`. `pos` is declared by the macro as `type *`.
**Note:** the current implementation caches the next pointer from `(head)->next->prev`, which equals `head` for a normal list, so the loop stops after visiting the first reversed node. Treat full reverse iteration as limited/buggy until the source macro is fixed.
#### `FIO_LIST_PUSH`
```c
#define FIO_LIST_PUSH(head, n) \
do { \
(n)->prev = (head)->prev; \
(n)->next = (head); \
(head)->prev->next = (n); \
(head)->prev = (n); \
} while (0)
```
Pushes node `n` to the end of the circular list, just before `head`.
Both `head` and `n` must point to initialized `FIO_LIST_NODE` / `FIO_LIST_HEAD` objects. The macro does not check whether `n` already belongs to another list.
#### `FIO_LIST_REMOVE`
```c
#define FIO_LIST_REMOVE(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
} while (0)
```
Removes `n` from its current list. The node's own `next` and `prev` fields are left pointing at the old neighbors.
#### `FIO_LIST_REMOVE_RESET`
```c
#define FIO_LIST_REMOVE_RESET(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
(n)->next = (n)->prev = (n); \
} while (0)
```
Removes `n`, then resets it to a self-linked node.
#### `FIO_LIST_POP`
```c
#define FIO_LIST_POP(type, node_name, dest_ptr, head) \
do { \
(dest_ptr) = FIO_PTR_FROM_FIELD(type, node_name, ((head)->next)); \
FIO_LIST_REMOVE(&(dest_ptr)->node_name); \
} while (0)
```
Removes the first object after `head` and stores the containing object pointer in `dest_ptr`.
Do not call this on an empty list. It will treat the head as a stored object and wander into undefined behavior.
#### `FIO_LIST_IS_EMPTY`
```c
#define FIO_LIST_IS_EMPTY(head) \
((!(head)) || ((!(head)->next) | ((head)->next == (head))))
```
Returns non-zero if `head` is `NULL`, `head->next` is `NULL`, or the head points to itself.
#### `FIO_INDEXED_LIST_PUSH`
```c
#define FIO_INDEXED_LIST_PUSH(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.prev = (root)[(head)].node_name.prev; \
(root)[n__].node_name.next = (head); \
(root)[(root)[(head)].node_name.prev].node_name.next = (n__); \
(root)[(head)].node_name.prev = (n__); \
} while (0)
```
Adds index `i` to the end of the indexed circular list, just before `head`. `head` is not changed.
`root` is the array containing all elements, and `node_name` is the embedded indexed-list node field.
#### `FIO_INDEXED_LIST_UNSHIFT`
```c
#define FIO_INDEXED_LIST_UNSHIFT(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.next = (root)[(head)].node_name.next; \
(root)[n__].node_name.prev = (head); \
(root)[(root)[(head)].node_name.next].node_name.prev = (n__); \
(root)[(head)].node_name.next = (n__); \
(head) = (n__); \
} while (0)
```
Adds index `i` to the beginning of the list and assigns `head = i`. The `head` argument must be an assignable lvalue.
#### `FIO_INDEXED_LIST_REMOVE`
```c
#define FIO_INDEXED_LIST_REMOVE(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
} while (0)
```
Removes index `i` from its current indexed list. The removed node keeps its previous links.
#### `FIO_INDEXED_LIST_REMOVE_RESET`
```c
#define FIO_INDEXED_LIST_REMOVE_RESET(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
(root)[n__].node_name.next = (root)[n__].node_name.prev = (n__); \
} while (0)
```
Removes index `i`, then resets its indexed node so `next == prev == i`.
#### `FIO_INDEXED_LIST_EACH`
```c
#define FIO_INDEXED_LIST_EACH(root, node_name, head, pos) \
for (size_t pos = (head), \
stooper___hd = (head), \
stopper___ils___ = 0, \
pos##___nxt = (root)[(head)].node_name.next; \
!stopper___ils___; \
(stopper___ils___ = ((pos = pos##___nxt) == stooper___hd)), \
pos##___nxt = (root)[pos].node_name.next)
```
Iterates forward over an indexed circular list, including `head`. `pos` is declared by the macro as `size_t`.
`head` must be a valid index. Empty-list sentinels are a caller convention, not something this macro handles.
#### `FIO_INDEXED_LIST_EACH_REVERSED`
```c
#define FIO_INDEXED_LIST_EACH_REVERSED(root, node_name, head, pos) \
for (size_t pos = ((root)[(head)].node_name.prev), \
pos##___nxt = \
((root)[((root)[(head)].node_name.prev)].node_name.prev), \
stooper___hd = (head), \
stopper___ils___ = 0; \
!stopper___ils___; \
((stopper___ils___ = (pos == stooper___hd)), \
(pos = pos##___nxt), \
(pos##___nxt = (root)[pos##___nxt].node_name.prev)))
```
Iterates backward over an indexed circular list, including the tail and eventually the head.
### Examples
#### Pointer list
```c
#include "fio-stl.h"
typedef struct item_s {
FIO_LIST_NODE node;
int value;
} item_s;
int main(void) {
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
item_s a = {.node = FIO_LIST_INIT(a.node), .value = 1};
item_s b = {.node = FIO_LIST_INIT(b.node), .value = 2};
FIO_LIST_PUSH(&list, &a.node);
FIO_LIST_PUSH(&list, &b.node);
int sum = 0;
FIO_LIST_EACH(item_s, node, &list, pos) {
sum += pos->value;
}
item_s *first = 0;
FIO_LIST_POP(item_s, node, first, &list);
return (sum == 3 && first == &a) ? 0 : 1;
}
```
#### Safe removal while iterating
```c
#include "fio-stl.h"
typedef struct item_s {
FIO_LIST_NODE node;
int value;
} item_s;
int main(void) {
FIO_LIST_HEAD list = FIO_LIST_INIT(list);
item_s a = {.node = FIO_LIST_INIT(a.node), .value = 1};
item_s b = {.node = FIO_LIST_INIT(b.node), .value = 2};
FIO_LIST_PUSH(&list, &a.node);
FIO_LIST_PUSH(&list, &b.node);
FIO_LIST_EACH(item_s, node, &list, pos) {
if (pos->value == 1)
FIO_LIST_REMOVE_RESET(&pos->node);
}
return FIO_LIST_IS_EMPTY(&list) ? 1 : 0;
}
```
#### Indexed list
```c
#include "fio-stl.h"
typedef struct slot_s {
FIO_INDEXED_LIST16_NODE node;
int value;
} slot_s;
int main(void) {
slot_s slots[3] = {{{0}}};
FIO_INDEXED_LIST16_HEAD head = 0;
slots[0].node.next = slots[0].node.prev = 0;
slots[0].value = 10;
slots[1].node.next = slots[1].node.prev = 1;
slots[1].value = 20;
slots[2].node.next = slots[2].node.prev = 2;
slots[2].value = 30;
FIO_INDEXED_LIST_PUSH(slots, node, head, 1);
FIO_INDEXED_LIST_PUSH(slots, node, head, 2);
int sum = 0;
FIO_INDEXED_LIST_EACH(slots, node, head, pos) {
sum += slots[pos].value;
}
FIO_INDEXED_LIST_REMOVE_RESET(slots, node, 1);
return (sum == 60 && slots[1].node.next == 1) ? 0 : 1;
}
```
### Safety Notes
#### Initialization
Every head or node must be initialized before use. Pointer lists have `FIO_LIST_INIT`; indexed lists do not have a dedicated init macro, so initialize an indexed node with:
```c
root[i].node.next = root[i].node.prev = i;
```
#### Empty lists
Pointer lists have a natural empty state: `head->next == head`. `FIO_LIST_IS_EMPTY` checks it.
Indexed lists usually reserve an out-of-range value, often `(type)-1`, to mean empty. The indexed macros do not check for this sentinel; call them only when `head` is a valid index.
#### Index width
The indexed macros use `size_t` temporaries, then store into `uint32_t`, `uint16_t`, or `uint8_t` node fields. Keep every index representable by the chosen node type.
#### Removal and iteration
The iteration macros cache the next node/index before running the loop body, so removing the current non-head item is supported. If you remove or reset the current head in an indexed list, update your `head` value deliberately.
#### Memory ownership
Lists own no memory. Removing or popping unlinks nodes only; it does not destroy containing objects.
#### Thread safety
Mutating a list is not atomic. Multiple threads may read immutable lists, but concurrent mutation needs external synchronization.
#### Macro expansion
All APIs here are macros. Arguments may be evaluated more than once, and helper variable names are introduced inside loops. Pass simple lvalues/pointers, not expressions with side effects.
# Logging and Assertions
```c
#define FIO_LOG
#include "fio-stl.h"
```
Heap-allocation-free logging macros and assertion utilities. `FIO_LOG2STDERR` and the `FIO_LOG_*` macros are functional when `FIO_LOG` or `FIO_LEAK_COUNTER` is defined; otherwise they are no-ops.
`FIO_LOG` uses `libc` functions (`vsnprintf`, `fwrite`) and the library helper `fio_memcpy32`. If you are building without `libc`, provide your own implementations or shadowing macros before including the module.
## Log Levels
```c
#define FIO_LOG_LEVEL_NONE 0 /* no logging */
#define FIO_LOG_LEVEL_FATAL 1 /* fatal errors */
#define FIO_LOG_LEVEL_ERROR 2 /* errors and above */
#define FIO_LOG_LEVEL_WARNING 3 /* warnings and above */
#define FIO_LOG_LEVEL_INFO 4 /* info and above */
#define FIO_LOG_LEVEL_DEBUG 5 /* everything, including debug */
```
## Log Level Control
### `FIO_LOG_LEVEL_SET`
```c
#define FIO_LOG_LEVEL_SET(new_level) fio___log_level_set(new_level)
```
Sets the application-wide logging level. Returns the new level.
### `FIO_LOG_LEVEL_GET`
```c
#define FIO_LOG_LEVEL_GET() ((fio___log_level()))
```
Returns the current logging level as an integer.
```c
FIO_LOG_LEVEL_SET(FIO_LOG_LEVEL_WARNING);
int level = FIO_LOG_LEVEL_GET(); /* 3 */
```
## Configuration
### `FIO_LOG_LEVEL_DEFAULT`
Initial level. Defaults to `FIO_LOG_LEVEL_INFO` unless `DEBUG` is defined and truthy (`defined(DEBUG) && DEBUG`), in which case it is `FIO_LOG_LEVEL_DEBUG`.
### `FIO_STDERR_FILE`
```c
#define FIO_STDERR_FILE stderr
```
Output destination for all logging. Define it before including `./fio-stl.h` to redirect logs to another `FILE*`.
### `FIO_LOG_LENGTH_LIMIT`
```c
#define FIO_LOG_LENGTH_LIMIT 1024
```
Log buffer size. Keep this above `128`.
- When `FIO_LOG_LENGTH_LIMIT > 128`, the formatted message is limited to `FIO_LOG_LENGTH_LIMIT - 34` bytes, followed by a 32-byte truncation warning.
- When `FIO_LOG_LENGTH_LIMIT <= 128`, `vsnprintf` is capped at `FIO_LOG_LENGTH_LIMIT - 2` bytes; the truncation warning is appended afterward.
- Values `<= 2` are dangerous: the `vsnprintf` size argument underflows/overflows.
## Core Logging Function
### `FIO_LOG2STDERR`
```c
void FIO_LOG2STDERR(const char *format, ...);
```
`printf`-style output to `FIO_STDERR_FILE` using only stack-allocated memory. Truncation follows the two `FIO_LOG_LENGTH_LIMIT` paths described above. In `./fio-stl/001 logging.h` the name is `#undef`-ed before the `static` function definition, so a pre-include macro is removed and a post-inclusion function definition would conflict. Override it after inclusion only by defining `FIO_LOG2STDERR` as a macro.
## Log Macros
Most macros check the current log level before printing. `FIO_LOG_WRITE` has no level check and always prefixes output with the file and line number.
| Macro | Level | Prefix |
|-------|-------|--------|
| `FIO_LOG_FATAL(...)` | ≥ `FATAL` | `FATAL:` (bold inverse) |
| `FIO_LOG_ERROR(...)` | ≥ `ERROR` | `ERROR:` (bold) |
| `FIO_LOG_SECURITY(...)` | ≥ `ERROR` | `SECURITY:` (bold) |
| `FIO_LOG_WARNING(...)` | ≥ `WARNING` | `WARNING:` (dim) |
| `FIO_LOG_INFO(...)` | ≥ `INFO` | `INFO:` |
| `FIO_LOG_DEBUG(...)` | ≥ `DEBUG` | `DEBUG:` + file:line |
| `FIO_LOG_DEBUG2(...)` | ≥ `DEBUG` | `DEBUG:` |
| `FIO_LOG_WRITE(...)` | always | file:line |
## Debug-Only Log Macros
These expand to their non-`D` counterparts when `DEBUG` is defined, and to no-ops otherwise.
- `FIO_LOG_DDEBUG(...)`
- `FIO_LOG_DDEBUG2(...)`
- `FIO_LOG_DERROR(...)`
- `FIO_LOG_DSECURITY(...)`
- `FIO_LOG_DWARNING(...)`
- `FIO_LOG_DINFO(...)`
## Assertions
### `FIO_ASSERT`
```c
#define FIO_ASSERT(cond, ...)
```
If `cond` is false, prints a fatal message, prints `errno` and its string, sends `SIGINT` in debug builds, and calls `exit(-1)`.
### `FIO_ASSERT_ALLOC`
```c
#define FIO_ASSERT_ALLOC(ptr) FIO_ASSERT((ptr), "memory allocation failed.")
```
Convenience wrapper for allocation failures.
### `FIO_ASSERT_DEBUG`
```c
#define FIO_ASSERT_DEBUG(cond, ...)
```
Active only when `DEBUG` is defined. On failure it behaves like `FIO_ASSERT`, printing the file and line number before terminating.
### `FIO_ASSERT_STATIC`
```c
#define FIO_ASSERT_STATIC(cond, msg)
```
Compile-time assertion. `cond` must be a constant expression; `msg` is a string literal with no format specifiers. Falls back to a sized-array trick on pre-C11 compilers.
See also: [Compiler Attributes](./001 compiler attributes.md).
## Leak Counter Helpers
Enabled by `FIO_LEAK_COUNTER` (default `1`). `FIO_NO_LOG` and `FIO_LEAK_COUNTER` are mutually exclusive because leak reports are printed through the log.
### `FIO_LEAK_COUNTER_DEF`
```c
#define FIO_LEAK_COUNTER_DEF(name)
```
Defines a named counter and a cleanup function that reports remaining allocations at process exit.
### `FIO_LEAK_COUNTER_ON_ALLOC`
```c
#define FIO_LEAK_COUNTER_ON_ALLOC(name)
```
Increment the named counter. Call after a successful allocation.
### `FIO_LEAK_COUNTER_ON_FREE`
```c
#define FIO_LEAK_COUNTER_ON_FREE(name)
```
Decrement the named counter. Call when memory is freed. Detects double-free.
### `FIO_LEAK_COUNTER_COUNT`
```c
#define FIO_LEAK_COUNTER_COUNT(name)
```
Returns the current value of the named counter.
### `FIO_LEAK_COUNTER_SKIP_EXIT`
```c
#define FIO_LEAK_COUNTER_SKIP_EXIT 0
```
Set to `1` to prevent automatic leak reporting at exit.
## Example
```c
#define FIO_LOG
#include "fio-stl.h"
int main(void) {
FIO_LOG_LEVEL_SET(FIO_LOG_LEVEL_WARNING);
FIO_LOG_INFO("not printed");
FIO_LOG_WARNING("number invalid: %d", 42);
FIO_LOG2STDERR("direct message");
void *ptr = malloc(100);
FIO_ASSERT_ALLOC(ptr);
FIO_ASSERT(ptr != NULL, "expected a pointer");
free(ptr);
return 0;
}
```
# Core Math Helpers
Low-level integer arithmetic, prime testing, and multi-precision primitives defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
These functions work on plain `uint64_t` arrays. The higher-level division, shift, and inversion helpers live in [`./002 math.h`](https://facil.io/edge/api/002-math/), and the wide-union wrappers are documented in [`./001 vector math.md`](https://facil.io/edge/vector-math/).
## Hash primes
Four families of pre-selected prime constants, indexed `0` through `31`:
```c
FIO_U8_HASH_PRIME0 ... FIO_U8_HASH_PRIME31
FIO_U16_HASH_PRIME0 ... FIO_U16_HASH_PRIME31
FIO_U32_HASH_PRIME0 ... FIO_U32_HASH_PRIME31
FIO_U64_HASH_PRIME0 ... FIO_U64_HASH_PRIME31
```
Each fits in the named unsigned type and has about half of its bits set (`3`–`5` bits for the 8-bit primes). They are useful as hash mixers and for picking small prime bases.
## Modular arithmetic
```c
uint64_t fio_math_mod_mul64(uint64_t a, uint64_t b, uint64_t mod);
```
Computes `(a * b) % mod` without overflowing 64 bits. Uses `__uint128_t` when available; otherwise falls back to a double-and-add loop.
```c
uint64_t fio_math_mod_expo64(uint64_t base, uint64_t exp, uint64_t mod);
```
Right-to-left binary modular exponentiation. Calls `fio_math_mod_mul64` for each set bit of `exp`.
## Primality tests
```c
bool fio_math_is_uprime(uint64_t n);
bool fio_math_is_iprime(int64_t n);
```
`fio_math_is_uprime` tests whether `n` is probably prime. `fio_math_is_iprime` tests the absolute value of `n`.
For `n < FIO_PRIME_TABLE_LIMIT` (default `1024`) the test is deterministic using a small sieve. Larger values use Miller–Rabin with `10` rounds. Internal helpers `fio___is_prime_table` and `fio___is_prime_maybe` implement the two paths.
## 64-bit add / sub / mul with carry
```c
uint64_t fio_math_addc64(uint64_t a, uint64_t b,
uint64_t carry_in, uint64_t *carry_out);
uint64_t fio_math_subc64(uint64_t a, uint64_t b,
uint64_t borrow_in, uint64_t *borrow_out);
uint64_t fio_math_mulc64(uint64_t a, uint64_t b,
uint64_t *carry_out);
```
Return the low 64 bits of `a + b + carry_in`, `a - b - borrow_in`, or `a * b`; the outgoing carry/borrow/high word is written to `*carry_out`. They use compiler builtins where available and fall back to portable word arithmetic.
## Multi-precision add / sub / mul
All multi-precision routines treat arrays as little-endian `uint64_t` words: index `0` holds the least-significant word. Inputs are expected to have the same `len` (pad shorter inputs with zero).
```c
bool fio_math_add(uint64_t *dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
uint64_t fio_math_sub(uint64_t *dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
void fio_math_mul(uint64_t *restrict dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
```
* `fio_math_add` writes `a + b` to `dest` and returns the final carry.
* `fio_math_sub` writes `a - b` to `dest` and returns the final borrow.
* `fio_math_mul` writes the full `len * 2` word product of `a` and `b` to `dest`.
When `__uint128_t` is available, `fio_math_addc128` and `fio_math_add2` provide the same carry-style addition for 128-bit words.
## Internal multiplication helpers
`fio_math_mul` dispatches to specialized helpers depending on `len`:
* `fio___math_mul_long` — optimized schoolbook multiplication, loop-unrolled, with a `__uint128_t` fast path.
* `fio___math_mul_256` — fully unrolled 4-word (256-bit) multiply.
* `fio___math_mul_512` — semi-unrolled 8-word (512-bit) multiply.
* `fio___math_mul_1024` — semi-unrolled 16-word (1024-bit) multiply, mostly used as a fallback.
For large numbers:
```c
#define FIO___MATH_KARATSUBA_THRESHOLD 32
```
When `len` is at least this threshold and even, `fio_math_mul` calls `fio___math_mul_karatsuba`.
```c
void fio___math_mul_karatsuba(uint64_t *restrict dest,
const uint64_t *a,
const uint64_t *b,
size_t len);
```
`fio___math_mul_karatsuba` splits `a` and `b` into low/high halves, computes `z0`, `z1`, and `z2`, then combines them as `z2*B^2 + z1*B + z0`. It uses `fio___math_mul_norecurse` for the three half-sized sub-products and allocates scratch buffers on the stack. `len` must be even; on MSVC or pre-C++14 the stack buffers are fixed at 256 words, so inputs are limited accordingly.
## See also
* [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth.
* [`./002 math.h`](https://facil.io/edge/api/002-math/) — higher-level division, shift, and inversion helpers.
* [`./001 vector math.md`](https://facil.io/edge/vector-math/) — wide-union `fio_uXXX` arithmetic built on these primitives.
# Memory Alternatives and OS Patches
Defines portable fallback memory routines and small OS compatibility patches. Implemented in [`./001 memalt.h`](https://facil.io/edge/api/001-memalt/) and [`./001 patches.h`](https://facil.io/edge/api/001-patches/); wired into the selector macros in [`./000 core.h`](https://facil.io/edge/api/000-core/).
## When `FIO_MEMALT` is used
Define `FIO_MEMALT` before including the STL to substitute standard-library memory and string calls with facil.io fallbacks:
```c
#define FIO_MEMALT
#include "fio-stl/include.h"
```
This is useful for freestanding or restricted environments, or when you want every memory call to go through the same portable implementation. The fallbacks are correct but usually slower than a good libc, so the default is to use compiler builtins or libc instead.
## Selector wiring
When `FIO_MEMALT` is defined, [`./000 core.h`](https://facil.io/edge/api/000-core/) routes any unset selector to the matching fallback:
| Selector | Fallback |
|----------|----------|
| `FIO_MEMCPY` | `fio_memcpy` |
| `FIO_MEMMOVE` | `fio_memcpy` |
| `FIO_MEMCMP` | `fio_memcmp` |
| `FIO_MEMCHR` | `fio_memchr` |
| `FIO_MEMSET` | `fio_memset` |
| `FIO_STRLEN` | `fio_strlen` |
If `FIO_MEMALT` is not defined, the selectors default to `__builtin_*` when available, otherwise to the standard `memcpy`, `memset`, `memchr`, `memcmp`, and `strlen`. You can also override any selector directly without enabling `FIO_MEMALT`.
## Fallback memory routines
All routines are declared in [`./001 memalt.h`](https://facil.io/edge/api/001-memalt/).
### `fio_memcpy`
```c
void *fio_memcpy(void *dest, const void *src, size_t bytes);
```
Copies `bytes` from `src` to `dest`. Handles overlapping regions like `memmove`. Returns `dest` immediately if `dest == src`, `bytes` is zero, or either pointer is `NULL`.
For small, non-overlapping copies (≤64 bytes) it uses overlapping fixed-width copies. For larger or overlapping regions it uses a 64-byte buffered copy, choosing forward or backward direction as needed.
### `fio_memset`
```c
void *fio_memset(void *restrict dest, uint64_t data, size_t bytes);
```
Fills `bytes` at `dest` with the 8-byte pattern `data`. If `data` is below `0x100`, it is broadcast to every byte to match classic `memset` behavior. Returns `dest`, or `NULL` if `dest` is `NULL`.
### `fio_memchr`
```c
void *fio_memchr(const void *buffer, const char token, size_t len);
```
Finds the first byte equal to `token` in the first `len` bytes of `buffer`. Returns a pointer to the match, or `NULL` if not found, `buffer` is `NULL`, or `len` is zero.
Picks the best available implementation at compile time:
- AVX2 on x86-64 with intrinsics
- SSE2 on x86 with intrinsics
- NEON on ARM with intrinsics
- Scalar 128-bit block fallback otherwise
### `fio_memcmp`
```c
int fio_memcmp(const void *a, const void *b, size_t len);
```
Compares the first `len` bytes of `a` and `b`. Returns `1` if `a > b`, `-1` if `a < b`, and `0` if equal. Returns `0` immediately if `a == b` or `len` is zero.
Processes 64-byte blocks for large buffers, 8-byte blocks for medium buffers, and byte-by-byte for the tail.
### `fio_strlen`
```c
size_t fio_strlen(const char *str);
```
Returns the length of the NUL-terminated string `str`, or `0` if `str` is `NULL`. The implementation is a simple byte loop the compiler can auto-vectorize. Building with Address Sanitizer may flag speculative reads.
## OS patch environment helpers
[`./001 patches.h`](https://facil.io/edge/api/001-patches/) provides `fio_sys_env` and `fio_sys_env_set` as portable replacements for `getenv` and `setenv`.
```c
char *fio_sys_env(const char *name);
int fio_sys_env_set(const char *name, const char *value, int overwrite);
```
- `fio_sys_env(name)` returns the value of the environment variable, or `NULL`.
- `fio_sys_env_set(name, value, overwrite)` sets the variable. If `overwrite` is zero and the variable already exists, it is left unchanged. Returns `0` on success, `-1` on failure.
On POSIX these are thin wrappers around `getenv`/`setenv`. On Windows they call `GetEnvironmentVariableA` and `SetEnvironmentVariableA` directly, bypassing macro redefinitions such as the one in Ruby's `win32.h` and avoiding `wchar_t` conversion issues.
## Other OS patches
[`./001 patches.h`](https://facil.io/edge/api/001-patches/) also fills small portability gaps:
- **macOS < 10.12**: defines a `clock_gettime` fallback using `gettimeofday`, plus `CLOCK_REALTIME` and `CLOCK_MONOTONIC` placeholders.
- **Windows startup**: enables virtual terminal processing for stdout/stderr and sets binary file mode by default.
- **Windows symbol gaps**: supplies inline patches for `strcasecmp`, `write`, `read`, `clock_gettime`, and `pwrite`; maps `O_*`/`S_*` constants, `fstat`/`stat`/`lseek`/`unlink`, and defines `PATH_MAX`.
- **POSIX branch**: present but currently has no extra patches beyond the environment helpers.
# Memory Primitives
Low-level memory helpers used everywhere else in the STL. Most are defined in [`./000 core.h`](https://facil.io/edge/api/000-core/); the allocator abstraction macros live in [`./001 header.h`](https://facil.io/edge/api/001-header/).
## Memory-Function Selector Macros
These macros route standard-library-style calls to either compiler builtins or libc functions. Define any of them before inclusion to override the default.
| Macro | Default | Use |
|-------|---------|-----|
| `FIO_MEMCPY(dest, src, n)` | `__builtin_memcpy` if available, else `memcpy` | Variable-length copy. |
| `FIO_MEMMOVE(dest, src, n)` | `__builtin_memmove` if available, else `memmove` | Overlap-safe copy. |
| `FIO_MEMSET(dest, c, n)` | `__builtin_memset` if available, else `memset` | Fill memory. |
| `FIO_MEMCHR(ptr, c, n)` | `__builtin_memchr` if available, else `memchr` | Find byte. |
| `FIO_STRLEN(str)` | `__builtin_strlen` if available, else `strlen` | C string length. |
| `FIO_MEMCMP(a, b, n)` | `__builtin_memcmp` if available, else `memcmp` | Compare memory. |
If `FIO_MEMALT` is defined, any selector that was not already set is routed to the matching facil.io fallback:
```c
#define FIO_MEMCPY fio_memcpy
#define FIO_MEMMOVE fio_memcpy
#define FIO_MEMCMP fio_memcmp
#define FIO_MEMCHR fio_memchr
#define FIO_MEMSET fio_memset
#define FIO_STRLEN fio_strlen
```
Those fallback routines are documented in `./001 memalt.h` (the memalt module).
## Fixed-Size Copy Helpers
These functions copy an exact number of bytes from `src` to `dest`. They are implemented with the fixed-size `FIO_MEMCPY` path when builtins are available, otherwise with a compact byte loop.
- When `__builtin_memcpy` is available, each helper returns `dest` (the `__builtin_memcpy` return value).
- In the non-builtin fallback, each helper returns `dest + n`.
`fio_memcpy0` always returns `dest`.
```c
void *fio_memcpy0 (void *restrict dest, const void *restrict src);
void *fio_memcpy1 (void *restrict dest, const void *restrict src);
void *fio_memcpy2 (void *restrict dest, const void *restrict src);
void *fio_memcpy3 (void *restrict dest, const void *restrict src);
void *fio_memcpy4 (void *restrict dest, const void *restrict src);
void *fio_memcpy5 (void *restrict dest, const void *restrict src);
void *fio_memcpy6 (void *restrict dest, const void *restrict src);
void *fio_memcpy7 (void *restrict dest, const void *restrict src);
void *fio_memcpy8 (void *restrict dest, const void *restrict src);
void *fio_memcpy16(void *restrict dest, const void *restrict src);
void *fio_memcpy32(void *restrict dest, const void *restrict src);
void *fio_memcpy64(void *restrict dest, const void *restrict src);
void *fio_memcpy128(void *restrict dest, const void *restrict src);
void *fio_memcpy256(void *restrict dest, const void *restrict src);
void *fio_memcpy512(void *restrict dest, const void *restrict src);
void *fio_memcpy1024(void *restrict dest, const void *restrict src);
void *fio_memcpy2048(void *restrict dest, const void *restrict src);
void *fio_memcpy4096(void *restrict dest, const void *restrict src);
```
### Tail helpers
`fio_memcpyNx` copies `len & N` bytes — useful for handling the tail end of a larger copy without explicit branches.
```c
void *fio_memcpy0x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy7x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy15x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy31x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy63x (void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy127x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy255x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy511x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy1023x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy2047x(void *restrict dest, const void *restrict src, size_t len);
void *fio_memcpy4095x(void *restrict dest, const void *restrict src, size_t len);
```
### Internal unsafe helpers
Two internal helpers back the tail functions. They assume the regions do not overlap and perform no validation.
```c
void *fio___memcpy_unsafe_63x(void *restrict dest, const void *restrict src, size_t len);
void *fio___memcpy_unsafe_x (void *restrict dest, const void *restrict src, size_t len);
```
- `fio___memcpy_unsafe_63x` is optimized for lengths up to 63 bytes.
- `fio___memcpy_unsafe_x` handles arbitrary lengths, copying 128-byte blocks (or 256-byte blocks when `FIO_LIMIT_INTRINSIC_BUFFER` is `0`) and finishing with a 64-byte tail.
These are `static`/`static inline` helpers; do not use them directly unless you are implementing a higher-level routine and have already checked the inputs.
## Unaligned Buffer Accessors
Read and write integers from byte buffers that may not be naturally aligned. The suffix controls byte order:
- `u` — native (unspecified) byte order.
- `_le` — little-endian.
- `_be` — big-endian / network byte order.
```c
uint8_t fio_buf2u8u (const void *buf);
uint8_t fio_buf2u8_le(const void *buf);
uint8_t fio_buf2u8_be(const void *buf);
void fio_u2buf8u (void *buf, uint8_t v);
void fio_u2buf8_le(void *buf, uint8_t v);
void fio_u2buf8_be(void *buf, uint8_t v);
uint16_t fio_buf2u16u (const void *buf);
uint16_t fio_buf2u16_le(const void *buf);
uint16_t fio_buf2u16_be(const void *buf);
void fio_u2buf16u (void *buf, uint16_t v);
void fio_u2buf16_le(void *buf, uint16_t v);
void fio_u2buf16_be(void *buf, uint16_t v);
uint32_t fio_buf2u24u (const void *buf);
uint32_t fio_buf2u24_le(const void *buf);
uint32_t fio_buf2u24_be(const void *buf);
void fio_u2buf24u (void *buf, uint32_t v);
void fio_u2buf24_le(void *buf, uint32_t v);
void fio_u2buf24_be(void *buf, uint32_t v);
uint32_t fio_buf2u32u (const void *buf);
uint32_t fio_buf2u32_le(const void *buf);
uint32_t fio_buf2u32_be(const void *buf);
void fio_u2buf32u (void *buf, uint32_t v);
void fio_u2buf32_le(void *buf, uint32_t v);
void fio_u2buf32_be(void *buf, uint32_t v);
uint64_t fio_buf2u64u (const void *buf);
uint64_t fio_buf2u64_le(const void *buf);
uint64_t fio_buf2u64_be(const void *buf);
void fio_u2buf64u (void *buf, uint64_t v);
void fio_u2buf64_le(void *buf, uint64_t v);
void fio_u2buf64_be(void *buf, uint64_t v);
size_t fio_buf2zu(const void *buf);
void fio_u2bufzu(void *buf, size_t v);
```
`fio_buf2u24u` / `fio_u2buf24u` and `fio_buf2zu` / `fio_u2bufzu` select the correct implementation at compile time based on host endianness and `size_t` width.
Example:
```c
uint8_t packet[8] = {0};
fio_u2buf32_be(packet + 2, 0x12345678);
uint32_t n = fio_buf2u32_be(packet + 2); // 0x12345678
```
## Memory Allocator Abstraction
The STL types use these macros instead of calling `malloc`/`free` directly, so a custom allocator can be plugged in by defining them before inclusion. They are reset and redefined in [`./001 header.h`](https://facil.io/edge/api/001-header/) for each inclusion cycle.
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) realloc((ptr), (new_size))
#define FIO_MEM_FREE(ptr, size) free((ptr))
#define FIO_MEM_ALIGNMENT_SIZE sizeof(long double)
#define FIO_MEM_REALLOC_IS_SAFE 0
```
- `FIO_MEM_REALLOC` — reallocates `ptr` from `old_size` to `new_size`, copying at least `copy_len` bytes if the object moves. `NULL` `ptr` acts like `malloc`; `new_size == 0` acts like `free`.
- `FIO_MEM_FREE` — frees memory allocated with `FIO_MEM_REALLOC`.
- `FIO_MEM_ALIGNMENT_SIZE` — the alignment the allocator guarantees.
- `FIO_MEM_REALLOC_IS_SAFE` — non-zero when the allocator zero-initializes returned memory.
If the custom `fio_malloc` allocator has already been included (`H___FIO_MALLOC___H` is defined), the defaults are routed to it instead:
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) fio_realloc2((ptr), (new_size), (copy_len))
#define FIO_MEM_FREE(ptr, size) fio_free((ptr))
#define FIO_MEM_ALIGNMENT_SIZE fio_malloc_alignment()
#define FIO_MEM_REALLOC_IS_SAFE fio_realloc_is_safe()
```
Define `FIO_MEM_RESET` before inclusion to force these defaults to be re-evaluated. Define `FIO_MEMORY_DISABLE` or `FIO_MALLOC_TMP_USE_SYSTEM` to force the system allocator for an inclusion cycle.
### Recursive variants
The underscore-suffixed macros exist for use during recursive `#include` cycles:
```c
FIO_MEM_REALLOC_
FIO_MEM_FREE_
FIO_MEM_ALIGNMENT_SIZE_
FIO_MEM_REALLOC_IS_SAFE_
```
They normally mirror the non-underscore versions. When `FIO_MALLOC_TMP_USE_SYSTEM` is set they are forced to the system `realloc`/`free` for that cycle only, while the non-underscore macros keep their outer meaning.
## Unaligned Memory Access Knob
```c
#define FIO_UNALIGNED_MEMORY_ACCESS_ENABLED 1
```
Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/). It is auto-detected: if `FIO_UNALIGNED_ACCESS` is `1` (the default) and the target CPU is known to tolerate unaligned access — x86, x86_64, ARM64, or another architecture that defines `__ARM_FEATURE_UNALIGNED` — then `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED` becomes `1`. Otherwise it is `0`.
Set `FIO_UNALIGNED_ACCESS` to `0` before inclusion to disable the detection attempt, or define `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED` directly to override the result.
## See Also
- `./001 memalt.h` (the memalt module) — variable-length fallback memory routines (`fio_memcpy`, `fio_memset`, `fio_memcmp`, `fio_memchr`, `fio_strlen`).
- [`./001 endian.md`](https://facil.io/edge/endian-and-byte-ordering/) — byte-order detection and swap helpers.
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth for copy helpers and the unaligned-access knob.
- [`./001 header.h`](https://facil.io/edge/api/001-header/) — source of truth for the allocator macros.
# OS Detection
[`./000 core.h`](https://facil.io/edge/api/000-core/) detects the host OS and defines a few portable helpers so the rest of the library can use one code path per platform.
## Platform macros
| Macro | POSIX value | Windows value | Notes |
|---|---|---|---|
| `FIO_OS_POSIX` | `1` | `0` | Set on Linux, macOS, BSD, and other `__unix__`/`__linux__`/`__APPLE__` systems. |
| `FIO_OS_WIN` | `0` | `1` | Set on Windows, including MinGW, MSYS2, Cygwin, and Borland. |
The Windows check is evaluated first, because MinGW/MSYS2 define both `_WIN32` and `__unix__`. Treating the system as Windows avoids calling POSIX socket functions on Winsock `SOCKET` handles.
After detection, `FIO_OS_WIN` always wins over `FIO_OS_POSIX`:
```c
#if FIO_OS_WIN
#undef FIO_OS_POSIX
#define FIO_OS_POSIX 0
#endif
```
## Unix-tools level
`FIO_HAVE_UNIX_TOOLS` tells the library how Unix-like the environment is:
- `0` — no Unix tools (pure MSVC).
- `1` — full POSIX.
- `2` — MinGW.
- `3` — Cygwin.
It is used to decide whether functions such as `pipe()`, `fcntl()`, and POSIX signal APIs are available.
## Process helpers
| Macro | POSIX | Windows |
|---|---|---|
| `fio_getpid` | `getpid` | `_getpid` |
| `FIO_KILL_SELF()` | `kill(0, SIGINT)` | `GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)` |
`FIO_KILL_SELF` is guarded by `#ifndef`, so you can define your own version before including the header. The default implementation is used by the assertion/debug machinery to deliver a signal to the running process.
## Thread sleep and yield
| Macro | POSIX | Windows |
|---|---|---|
| `FIO_THREAD_WAIT(nano_sec)` | `nanosleep` | `Sleep` |
| `FIO_THREAD_YIELD()` | see below | see below |
| `FIO_THREAD_RESCHEDULE()` | `FIO_THREAD_WAIT(4)` | `FIO_THREAD_WAIT(4)` |
`FIO_THREAD_WAIT(nano_sec)` sleeps the current thread for the requested number of nanoseconds. On POSIX it builds a `struct timespec` and calls `nanosleep`. On Windows it converts the interval to milliseconds and calls `Sleep`; any non-zero request is rounded up to at least 1 ms.
`FIO_THREAD_YIELD()` hints the processor that the thread is in a spin loop. On x86/x86-64 with GCC or Clang it emits a `pause` instruction; on ARM/AArch64 with GCC or Clang it emits a `yield` instruction; with MSVC it calls `YieldProcessor()`; otherwise it falls back to `sched_yield()` on POSIX.
`FIO_THREAD_RESCHEDULE()` yields the thread by sleeping for 4 nanoseconds (`FIO_THREAD_WAIT(4)`). It is used by the lock code to back off busy waits without fully suspending the thread.
## Portable type patches
On pure MSVC `ssize_t` is missing, so the core header defines it from `SSIZE_T`:
```c
typedef SSIZE_T ssize_t;
```
`SSIZE_MAX` and `SSIZE_MIN` are also provided if the system headers do not:
```c
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t)((~(size_t)0) >> 1))
#endif
#ifndef SSIZE_MIN
#define SSIZE_MIN ((ssize_t)(~((~(size_t)0) >> 1)))
#endif
```
## `printf`-style format checking
The `FIO___PRINTF_STYLE(string_index, check_index)` macro is defined differently on each toolchain so the compiler can check `printf`-style arguments. MinGW/Cygwin use the MinGW format attribute, pure MSVC leaves it empty, and POSIX/GCC/Clang use the standard `printf` format attribute. It is documented in [`./001 compiler attributes.md`](https://facil.io/edge/compiler-attributes/).
# Core Random PRNG
An optionally-deterministic 128-bit pseudo-random number generator defined by a macro. It is a low-level building block for hash functions, test harnesses, and other internals that need an optionally repeatable, fast stream of bits.
For the high-level random module — `fio_rand64`, secure bytes, hashing, etc. — see [`./002 random.h`](https://facil.io/edge/api/002-random/).
The implementation is in [`./000 core.h`](https://facil.io/edge/api/000-core/).
## `fio_cycle_counter`
```c
uint64_t fio_cycle_counter(void);
```
Returns a hardware cycle counter when one is available, or a number otherwise.
In non-deterministic mode, the PRNG uses this for timing-based jitter during reseeding.
## `FIO_DEFINE_RANDOM128_FN`
```c
#define FIO_DEFINE_RANDOM128_FN(extern, name, reseed_log, seed_offset)
```
Defines a named PRNG instance and its API. The most common deterministic form looks like:
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0x1234)
```
Parameters:
- `extern` — linkage prefix, e.g. `static` or `FIO_SFUNC`.
- `name` — base name for all generated functions and internal state.
- `reseed_log` — automatic reseed interval, `2^reseed_log` calls. Set to `0` for a fully deterministic stream.
- `seed_offset` — arbitrary offset added to the default seed constants.
### Non-Deterministic Mode
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 5, 0x1234)
```
When `reseed_log` is non-zero (and less than `64`), the generator calls `name_reseed()` automatically every `2^reseed_log` calls, mixing in timing data and the cycle counter. This makes the stream non-deterministic, and improves entropy.
In this example, reseeding is set to `5`, resulting in reseeding every 32 calls (collecting new entropy every 4,096 bytes).
## Generated API
For a definition `FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0)` the following functions are generated:
### `my_rng128`
```c
fio_u128 my_rng128(void);
```
Returns 128 pseudo-random bits. The return type is [`fio_u128`](https://facil.io/edge/vector-math/), so the result can also be read through `.u64[0]`, `.u64[1]`, `.u8`, etc.
### `my_rng64`
```c
uint64_t my_rng64(void);
```
Returns 64 pseudo-random bits. Each call consumes half of a 128-bit result; a new 128-bit value is generated only every other call.
### `my_rng_bytes`
```c
void my_rng_bytes(void *dest, size_t len);
```
Fills `dest` with `len` pseudo-random bytes. Does nothing if `dest` is `NULL` or `len` is zero.
### `my_rng_reset`
```c
void my_rng_reset(void);
```
Resets the generator to its initial seeded state.
### `my_rng_reseed`
```c
void my_rng_reseed(void);
```
Re-seeds the generator using `CLOCK_MONOTONIC`, address/bits of local state, and `fio_cycle_counter()`. Useful after `fork()` or when you want to de-correlate separate processes.
### `my_rng_on_fork`
```c
void my_rng_on_fork(void *is_null);
```
Calls `my_rng_reseed()`. Fork-handler helper that takes an ignored `void *` argument.
## Example
```c
FIO_DEFINE_RANDOM128_FN(static, my_rng, 0, 0)
void roll_die(int n) {
my_rng_reset();
for (int i = 0; i < n; ++i) {
uint64_t r = my_rng64();
printf("roll %d: %llu\n", i, (unsigned long long)(r % 6 + 1));
}
}
```
Because `reseed_log` is `0`, the same seed always produces the same sequence.
## Determinism and fork safety
- Set `reseed_log = 0` for deterministic, repeatable output.
- Set `reseed_log` to a practical value such as `31` for a non-deterministic stream that reseeds automatically.
- After `fork()`, call `name_reseed()` or register a wrapper that calls `name_on_fork()` with `pthread_atfork()` to avoid duplicated sequences in child processes.
## Testing
Test results for this generator are recorded in [`./721 rand128 macro.md`](https://facil.io/edge/pseudo-random-and-hashing/).
## See also
- [`./002 random.h`](https://facil.io/edge/api/002-random/) — high-level random/hashing module.
- [`./001 vector math.md`](https://facil.io/edge/vector-math/) — the `fio_u128` return type.
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth.
# Fake x86 SIMD
```c
#define FIO_FAKE_X86 /* or FIO_FAKE_X86_SHADOW */
#include "fio-stl.h"
```
Portable x86 SIMD intrinsics for code that wants the x86 spell book even when the CPU does not. The implementation lives in [`./001 fx86.h`](https://github.com/facil-io/cstl001%20fx86.h) and uses the core wide unions from [`./000 core.h`](https://facil.io/edge/api/000-core/), especially `fio_u128` and `fio_u256`.
The module is compiled only when `FIO_FAKE_X86` or `FIO_FAKE_X86_SHADOW` is defined before including the STL.
### Configuration Macros
#### `FIO_FAKE_X86`
```c
#define FIO_FAKE_X86
```
Enables the namespaced fake-intrinsic API: `fio_fx86_*` and `fio_fx86_256_*`.
On real x86 with `FIO___HAS_X86_INTRIN`, supported functions pass through to native intrinsics when the matching feature macro exists (`__SSE2__`, `__AES__`, `__AVX2__`, and friends). Otherwise the fallback code copies through `fio_u128` / `fio_u256` and performs lane-wise C operations.
#### `FIO_FAKE_X86_SHADOW`
```c
#define FIO_FAKE_X86_SHADOW
```
Enables the same API as `FIO_FAKE_X86`, then shadows real intrinsic names with macros:
```c
#define __m128i fio_fx86_m128i
#define __m256i fio_fx86_m256i
#define _mm_xor_si128(a, b) fio_fx86_xor_si128((a), (b))
#define _mm256_xor_si256(a, b) fio_fx86_256_xor_si256((a), (b))
```
Shadow mode is meant for compiling x86 code paths on non-x86 machines. It also undefines ARM intrinsic guard macros such as `FIO___HAS_ARM_INTRIN`, `FIO___HAS_ARM_SHA3_INTRIN`, `__ARM_NEON`, and `__ARM_FEATURE_CRYPTO`, then defines the x86 feature guards used by downstream code (`FIO___HAS_X86_INTRIN`, `__SSE2__`, `__SSSE3__`, `__SSE4_1__`, `__AES__`, `__PCLMUL__`, `__SHA__`, `__SHA512__`, `__AVX2__`, etc.).
Shadowed names are macros. Keep arguments simple and avoid depending on compiler-specific intrinsic types outside the code path being shadowed.
#### `FIO___HAS_X86_INTRIN`
```c
#define FIO___HAS_X86_INTRIN 1
#include
```
Core detection flag from `000 core.h`. It is defined on x86_64 when intrinsics are allowed and `immintrin.h` is available.
Intrinsic detection is disabled when `DEBUG` or `NO_INTRIN` is defined.
#### `FIO___HAS_ARM_INTRIN`
```c
#define FIO___HAS_ARM_INTRIN 1
#include
#include
```
Core detection flag from `000 core.h`. It is defined when ARM crypto + NEON headers are available. `FIO_FAKE_X86_SHADOW` undefines it so fake x86 code paths can be selected instead of ARM paths.
#### `FIO_FOR_UNROLL_GROUP_SIZE`
```c
#define FIO_FOR_UNROLL_GROUP_SIZE ((size_t)256U)
```
The byte stride used by `FIO_FOR_UNROLL` for compiler-friendly batches.
Most CPUs should be able to hold at least 512 bytes in their registers. The default value assumes half that is available for the optimized loop, as the action within each loop will likely require some available registers.
#### `FIO_FOR_UNROLL`
```c
#define FIO_FOR_UNROLL(iterations, size_of_loop, i, action) \
do { \
size_t i = 0; \
const size_t fio___unroll_remainder__ = \
((iterations) & ((FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)) - 1)); \
if (fio___unroll_remainder__) \
for (; i < fio___unroll_remainder__; ++i) \
action; \
if (iterations) \
for (; !((iterations) + 1) || (i < (iterations));) \
for (size_t j__loop__ = 0; \
j__loop__ < (FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)); \
++j__loop__, ++i) \
action; \
} while (0)
```
Runs a remainder loop, then fixed 256-byte batches. The macro declares `size_t i` inside the expansion; `action` may use that name.
#### `_MM_HINT_T0`, `_MM_HINT_T1`, `_MM_HINT_T2`, `_MM_HINT_NTA`
```c
#define _MM_HINT_T0 3
#define _MM_HINT_T1 2
#define _MM_HINT_T2 1
#define _MM_HINT_NTA 0
```
Defined in fake mode when native x86 types are not present, so `fio_fx86_prefetch` and shadowed `_mm_prefetch` calls can compile.
### Types
#### `fio_u128`
```c
typedef union fio_u128 {
FIO___UXXX_UGRP_DEF(128);
#if defined(__SIZEOF_INT128__)
__uint128_t alignment_for_u128_[1];
#endif
} fio_u128 FIO_ALIGN(16);
```
The 128-bit storage union used by the fallback implementation. It exposes unsigned, signed, floating-point, and vector views (`u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, `x8`, `x16`, `x32`, `x64`, etc.).
#### `fio_u256`
```c
typedef union fio_u256 {
fio_u128 u128[2];
FIO___UXXX_UGRP_DEF(256);
#if defined(__SIZEOF_INT256__)
__uint256_t alignment_for_u256_[1];
#endif
} fio_u256 FIO_ALIGN(32);
```
The 256-bit storage union used by AVX2 and SHA-512 fallbacks. It includes all `fio_u128`-style views plus `u128[2]`.
#### `FIO_UXXX_X64_DEF`, `FIO_UXXX_X32_DEF`, `FIO_UXXX_X16_DEF`, `FIO_UXXX_X8_DEF`
```c
#define FIO_UXXX_X64_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X32_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X16_DEF(name, bits) /* platform-specific */
#define FIO_UXXX_X8_DEF(name, bits) /* platform-specific */
```
Core vector-member definition macros. On ARM they use NEON vector arrays. On GCC/Clang with `vector_size`, they use a single full-width vector. Otherwise they are plain C arrays. The fake x86 API itself uses `fio_u128` / `fio_u256` conversions instead of assuming a specific vector layout.
#### `fio_fx86_m128i`
```c
typedef __m128i fio_fx86_m128i;
/* or */
typedef fio_u128 fio_fx86_m128i;
```
The canonical 128-bit integer SIMD type for this module. It is native `__m128i` when available, otherwise `fio_u128`.
#### `fio_fx86_m256i`
```c
typedef __m256i fio_fx86_m256i;
/* or */
typedef fio_u256 fio_fx86_m256i;
```
The canonical 256-bit integer SIMD type for this module. It is native `__m256i` when available, otherwise `fio_u256`.
#### `FIO___FX86_HAS_NATIVE_M128I`, `FIO___FX86_HAS_NATIVE_M256I`, `FIO___FX86_HAS_NATIVE_TYPES`
```c
#define FIO___FX86_HAS_NATIVE_M128I 1 /* or 0 */
#define FIO___FX86_HAS_NATIVE_M256I 1 /* or 0 */
#define FIO___FX86_HAS_NATIVE_TYPES \
(FIO___FX86_HAS_NATIVE_M128I && FIO___FX86_HAS_NATIVE_M256I)
```
Internal feature flags used to choose native x86 types vs. `fio_uXXX` unions.
### API Functions
#### `fio___fx86_to_u128`
```c
FIO_IFUNC fio_u128 fio___fx86_to_u128(fio_fx86_m128i v);
```
Copies a `fio_fx86_m128i` into a `fio_u128` using `fio_memcpy16`. Use this when you need portable lane access and the type might be native `__m128i`.
#### `fio___fx86_from_u128`
```c
FIO_IFUNC fio_fx86_m128i fio___fx86_from_u128(fio_u128 u);
```
Copies a `fio_u128` into the canonical 128-bit fake x86 type.
#### `fio___fx86_to_u256`
```c
FIO_IFUNC fio_u256 fio___fx86_to_u256(fio_fx86_m256i v);
```
Copies a `fio_fx86_m256i` into a `fio_u256` using `fio_memcpy32`.
#### `fio___fx86_from_u256`
```c
FIO_IFUNC fio_fx86_m256i fio___fx86_from_u256(fio_u256 u);
```
Copies a `fio_u256` into the canonical 256-bit fake x86 type.
#### `fio_fx86_loadu_si128`, `fio_fx86_load_si128`, `fio_fx86_storeu_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_loadu_si128(const void *p);
FIO_IFUNC fio_fx86_m128i fio_fx86_load_si128(const void *p);
FIO_IFUNC void fio_fx86_storeu_si128(void *p, fio_fx86_m128i a);
```
SSE2-style 128-bit load/store helpers. The fallback copies 16 bytes. The aligned load name mirrors `_mm_load_si128`; the fallback does not check alignment.
#### `fio_fx86_add_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_add_epi32(fio_fx86_m128i a,
fio_fx86_m128i b);
```
Adds four unsigned 32-bit lanes with normal wraparound.
#### `fio_fx86_xor_si128`, `fio_fx86_or_si128`, `fio_fx86_and_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_xor_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_or_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_and_si128(fio_fx86_m128i a,
fio_fx86_m128i b);
```
Bitwise operations over the whole 128-bit value.
#### `fio_fx86_slli_si128`, `fio_fx86_srli_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_si128(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_si128(fio_fx86_m128i a, int imm8);
```
Shifts the whole 128-bit register by bytes, filling with zero. Values greater than or equal to 16 return zero in the fallback.
#### `fio_fx86_slli_epi32`, `fio_fx86_srli_epi32`, `fio_fx86_slli_epi64`, `fio_fx86_srli_epi64`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_epi32(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_epi32(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_slli_epi64(fio_fx86_m128i a, int imm8);
FIO_IFUNC fio_fx86_m128i fio_fx86_srli_epi64(fio_fx86_m128i a, int imm8);
```
Per-lane logical shifts for 32-bit and 64-bit lanes. Fallback shifts at or past the lane width produce zero.
#### `fio_fx86_shuffle_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_shuffle_epi32(fio_fx86_m128i a, int imm8);
```
Selects each output 32-bit lane from `a` using two bits per lane from `imm8`.
#### `fio_fx86_cmpeq_epi8`, `fio_fx86_movemask_epi8`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_cmpeq_epi8(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC int fio_fx86_movemask_epi8(fio_fx86_m128i a);
```
Byte equality produces `0xFF` or `0x00` per byte. The movemask packs the high bit of each byte into a 16-bit integer result.
#### `fio_fx86_set_epi32`, `fio_fx86_set_epi64x`, `fio_fx86_set_epi8`, `fio_fx86_set1_epi8`, `fio_fx86_setzero_si128`, `fio_fx86_cvtsi32_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi32(int e3, int e2, int e1, int e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi64x(int64_t e1, int64_t e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set_epi8(char e15, char e14, char e13,
char e12, char e11, char e10,
char e9, char e8, char e7, char e6,
char e5, char e4, char e3, char e2,
char e1, char e0);
FIO_IFUNC fio_fx86_m128i fio_fx86_set1_epi8(char a);
FIO_IFUNC fio_fx86_m128i fio_fx86_setzero_si128(void);
FIO_IFUNC fio_fx86_m128i fio_fx86_cvtsi32_si128(int a);
```
SSE2 constructors. The `set` functions follow x86 argument order: high lanes first, low lanes last.
#### `fio_fx86_shuffle_epi8`, `fio_fx86_alignr_epi8`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_shuffle_epi8(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_alignr_epi8(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
```
SSSE3 helpers. `shuffle_epi8` performs byte lookup, zeroing output bytes whose control byte has bit 7 set. `alignr_epi8` concatenates `b:a`, shifts right by `imm8` bytes, and returns 16 bytes.
#### `fio_fx86_blend_epi16`, `fio_fx86_extract_epi32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_blend_epi16(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
FIO_IFUNC int fio_fx86_extract_epi32(fio_fx86_m128i a, int imm8);
```
SSE4.1 helpers. `blend_epi16` selects each 16-bit lane from `a` or `b` by `imm8` bits. `extract_epi32` returns lane `imm8 & 3`.
#### `fio_fx86_prefetch`
```c
FIO_IFUNC void fio_fx86_prefetch(const void *p, int hint);
```
Prefetches on native SSE2. It is a no-op in fake mode.
#### `fio_fx86_aesenc_si128`, `fio_fx86_aesenclast_si128`, `fio_fx86_aeskeygenassist_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_aesenc_si128(fio_fx86_m128i a,
fio_fx86_m128i roundkey);
FIO_IFUNC fio_fx86_m128i fio_fx86_aesenclast_si128(fio_fx86_m128i a,
fio_fx86_m128i roundkey);
FIO_IFUNC fio_fx86_m128i fio_fx86_aeskeygenassist_si128(fio_fx86_m128i a,
int imm8);
```
AES-NI helpers. The fallback implements AES round logic with an S-box table. It is correct for portability tests, but the table lookups are cache-dependent; do not treat fake AES-NI as constant-time production crypto.
#### `fio_fx86_clmulepi64_si128`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_clmulepi64_si128(fio_fx86_m128i a,
fio_fx86_m128i b,
int imm8);
```
PCLMULQDQ carry-less multiplication. `imm8 & 1` selects the 64-bit lane from `a`; `(imm8 >> 4) & 1` selects the lane from `b`. The result is a 128-bit GF(2) product.
#### `fio_fx86_sha256rnds2_epu32`, `fio_fx86_sha256msg1_epu32`, `fio_fx86_sha256msg2_epu32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256rnds2_epu32(fio_fx86_m128i cdgh,
fio_fx86_m128i abef,
fio_fx86_m128i msg);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256msg1_epu32(fio_fx86_m128i msg0,
fio_fx86_m128i msg1);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha256msg2_epu32(fio_fx86_m128i msg4,
fio_fx86_m128i msg1);
```
SHA-NI SHA-256 helpers. The round function expects `cdgh`, `abef`, and message-plus-constant layout matching Intel SHA extensions.
#### `fio_fx86_sha1rnds4_epu32`, `fio_fx86_sha1nexte_epu32`, `fio_fx86_sha1msg1_epu32`, `fio_fx86_sha1msg2_epu32`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1rnds4_epu32(fio_fx86_m128i abcd,
fio_fx86_m128i e0,
int func);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1nexte_epu32(fio_fx86_m128i a,
fio_fx86_m128i b);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1msg1_epu32(fio_fx86_m128i msg0,
fio_fx86_m128i msg1);
FIO_IFUNC fio_fx86_m128i fio_fx86_sha1msg2_epu32(fio_fx86_m128i msg5,
fio_fx86_m128i msg1);
```
SHA-NI SHA-1 helpers. `func & 3` selects Ch, Parity, Maj, or Parity for `sha1rnds4`.
#### `fio_fx86_256_loadu_si256`, `fio_fx86_256_load_si256`, `fio_fx86_256_storeu_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_loadu_si256(const void *p);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_load_si256(const void *p);
FIO_IFUNC void fio_fx86_256_storeu_si256(void *p, fio_fx86_m256i a);
```
AVX2-style 256-bit load/store helpers. The fallback copies 32 bytes.
#### `fio_fx86_256_add_epi16`, `fio_fx86_256_sub_epi16`, `fio_fx86_256_mullo_epi16`, `fio_fx86_256_add_epi32`, `fio_fx86_256_sub_epi32`, `fio_fx86_256_mullo_epi32`, `fio_fx86_256_add_epi64`, `fio_fx86_256_sub_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_mullo_epi16(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_mullo_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_add_epi64(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sub_epi64(fio_fx86_m256i a,
fio_fx86_m256i b);
```
AVX2 packed integer arithmetic. Operations wrap by lane width; `mullo` keeps the low half of each product.
#### `fio_fx86_256_slli_epi32`, `fio_fx86_256_srai_epi32`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_slli_epi32(fio_fx86_m256i a, int imm8);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_srai_epi32(fio_fx86_m256i a, int imm8);
```
Per-lane 32-bit shifts. `srai` is arithmetic right shift; fallback values at or past 32 bits become the sign bit extended value.
#### `fio_fx86_256_xor_si256`, `fio_fx86_256_and_si256`, `fio_fx86_256_or_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_xor_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_and_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_or_si256(fio_fx86_m256i a,
fio_fx86_m256i b);
```
Bitwise operations over the whole 256-bit value.
#### `fio_fx86_256_cmpeq_epi8`, `fio_fx86_256_movemask_epi8`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_cmpeq_epi8(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC int fio_fx86_256_movemask_epi8(fio_fx86_m256i a);
```
AVX2 byte equality and 32-bit byte movemask.
#### `fio_fx86_256_set1_epi8`, `fio_fx86_256_set1_epi16`, `fio_fx86_256_set1_epi32`, `fio_fx86_256_set1_epi64x`, `fio_fx86_256_set_epi64x`, `fio_fx86_256_set_epi8`, `fio_fx86_256_setzero_si256`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi8(char a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi16(short a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi32(int a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set1_epi64x(long long a);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set_epi64x(long long e3,
long long e2,
long long e1,
long long e0);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_set_epi8(char e31, char e30, char e29,
char e28, char e27, char e26,
char e25, char e24, char e23,
char e22, char e21, char e20,
char e19, char e18, char e17,
char e16, char e15, char e14,
char e13, char e12, char e11,
char e10, char e9, char e8,
char e7, char e6, char e5,
char e4, char e3, char e2,
char e1, char e0);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_setzero_si256(void);
```
AVX2 constructors. Like Intel intrinsics, the explicit `set` functions receive high lanes first and low lanes last.
#### `fio_fx86_256_shuffle_epi8`, `fio_fx86_256_alignr_epi8`, `fio_fx86_256_permute4x64_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_shuffle_epi8(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_alignr_epi8(fio_fx86_m256i a,
fio_fx86_m256i b,
int imm8);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_permute4x64_epi64(fio_fx86_m256i a,
int imm8);
```
AVX2 shuffle/permute helpers. `shuffle_epi8` and `alignr_epi8` operate inside each 128-bit lane. `permute4x64_epi64` selects among the four 64-bit lanes across the full 256-bit value.
#### `fio_fx86_256_packs_epi32`, `fio_fx86_256_cvtepi16_epi32`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_packs_epi32(fio_fx86_m256i a,
fio_fx86_m256i b);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_cvtepi16_epi32(fio_fx86_m128i a);
```
AVX2 pack/convert helpers. `packs_epi32` saturates 32-bit signed lanes to 16-bit signed lanes, per 128-bit lane. `cvtepi16_epi32` sign-extends eight 16-bit values from the low 128 bits into eight 32-bit lanes.
#### `fio_fx86_256_castsi256_si128`, `fio_fx86_256_extracti128_si256`
```c
FIO_IFUNC fio_fx86_m128i fio_fx86_256_castsi256_si128(fio_fx86_m256i a);
FIO_IFUNC fio_fx86_m128i fio_fx86_256_extracti128_si256(fio_fx86_m256i a,
int imm8);
```
Extract 128-bit halves from a 256-bit value. `extracti128` uses `imm8 & 1`.
#### `fio_fx86_256_sha512rnds2_epi64`, `fio_fx86_256_sha512msg1_epi64`, `fio_fx86_256_sha512msg2_epi64`
```c
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512rnds2_epi64(fio_fx86_m256i state1,
fio_fx86_m256i state0,
fio_fx86_m256i wk);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512msg1_epi64(fio_fx86_m256i w0,
fio_fx86_m256i w1);
FIO_IFUNC fio_fx86_m256i fio_fx86_256_sha512msg2_epi64(fio_fx86_m256i w0,
fio_fx86_m256i w1);
```
Intel SHA-512 extension helpers using four 64-bit lanes.
### Shadowed Intrinsic Families
#### `_mm_*`
With `FIO_FAKE_X86_SHADOW`, these SSE/SHA/AES names are mapped to the matching `fio_fx86_*` functions:
```c
_mm_loadu_si128, _mm_load_si128, _mm_storeu_si128,
_mm_add_epi32,
_mm_xor_si128, _mm_or_si128, _mm_and_si128,
_mm_slli_si128, _mm_srli_si128,
_mm_slli_epi32, _mm_srli_epi32,
_mm_slli_epi64, _mm_srli_epi64,
_mm_shuffle_epi32, _mm_shuffle_epi8, _mm_alignr_epi8,
_mm_cmpeq_epi8, _mm_movemask_epi8,
_mm_set_epi32, _mm_set_epi64x, _mm_set_epi8,
_mm_set1_epi8, _mm_setzero_si128, _mm_cvtsi32_si128,
_mm_blend_epi16, _mm_extract_epi32, _mm_prefetch,
_mm_aesenc_si128, _mm_aesenclast_si128, _mm_aeskeygenassist_si128,
_mm_clmulepi64_si128,
_mm_sha256rnds2_epu32, _mm_sha256msg1_epu32, _mm_sha256msg2_epu32,
_mm_sha1rnds4_epu32, _mm_sha1nexte_epu32,
_mm_sha1msg1_epu32, _mm_sha1msg2_epu32
```
#### `_mm256_*`
With `FIO_FAKE_X86_SHADOW`, these AVX2/SHA-512 names are mapped to the matching `fio_fx86_256_*` functions:
```c
_mm256_loadu_si256, _mm256_load_si256, _mm256_storeu_si256,
_mm256_add_epi16, _mm256_sub_epi16, _mm256_mullo_epi16,
_mm256_add_epi32, _mm256_sub_epi32, _mm256_mullo_epi32,
_mm256_add_epi64, _mm256_sub_epi64,
_mm256_slli_epi32, _mm256_srai_epi32,
_mm256_xor_si256, _mm256_and_si256, _mm256_or_si256,
_mm256_cmpeq_epi8, _mm256_movemask_epi8,
_mm256_set1_epi8, _mm256_set1_epi16, _mm256_set1_epi32,
_mm256_set1_epi64x, _mm256_set_epi64x, _mm256_set_epi8,
_mm256_setzero_si256,
_mm256_shuffle_epi8, _mm256_alignr_epi8, _mm256_permute4x64_epi64,
_mm256_packs_epi32, _mm256_cvtepi16_epi32,
_mm256_castsi256_si128, _mm256_extracti128_si256,
_mm256_sha512rnds2_epi64, _mm256_sha512msg1_epi64,
_mm256_sha512msg2_epi64
```
### Examples
#### 128-bit add
```c
#define FIO_FAKE_X86
#include "fio-stl.h"
#include
int main(void) {
uint32_t a_in[4] = {1, 2, 3, 4};
uint32_t b_in[4] = {10, 20, 30, 40};
uint32_t out[4];
fio_fx86_m128i a = fio_fx86_loadu_si128(a_in);
fio_fx86_m128i b = fio_fx86_loadu_si128(b_in);
fio_fx86_m128i c = fio_fx86_add_epi32(a, b);
fio_fx86_storeu_si128(out, c);
return (out[0] == 11 && out[3] == 44) ? 0 : 1;
}
```
#### AVX2-style byte equality
```c
#define FIO_FAKE_X86
#include "fio-stl.h"
#include
int main(void) {
uint8_t a[32] = {0};
uint8_t b[32] = {0};
b[7] = 1;
fio_fx86_m256i va = fio_fx86_256_loadu_si256(a);
fio_fx86_m256i vb = fio_fx86_256_loadu_si256(b);
fio_fx86_m256i eq = fio_fx86_256_cmpeq_epi8(va, vb);
int mask = fio_fx86_256_movemask_epi8(eq);
return ((mask & (1 << 7)) == 0) ? 0 : 1;
}
```
#### Shadowing x86 names
```c
#define FIO_FAKE_X86_SHADOW
#include "fio-stl.h"
#include
int main(void) {
uint32_t in[4] = {0x01020304, 0, 0, 0};
uint32_t out[4];
__m128i v = _mm_loadu_si128((const __m128i *)in);
v = _mm_slli_epi32(v, 8);
_mm_storeu_si128((__m128i *)out, v);
return out[0] == 0x02030400 ? 0 : 1;
}
```
### Safety Notes
#### Memory ownership
The fake x86 API does not allocate or free memory. Load and store functions copy fixed-size byte ranges from caller-owned buffers.
#### Thread safety
Functions are value-based and use only caller-provided memory plus read-only tables. They are thread-safe when callers synchronize shared buffers as usual.
#### Constant-time behavior
The portable AES fallback uses lookup tables and is not constant-time. SHA and PCLMUL fallbacks are portable C implementations intended for correctness and test coverage, not a promise of native-intrinsic timing.
#### Macro expansion
`FIO_FOR_UNROLL` declares loop variables inside the macro and expands `action` many times. Shadow-mode `_mm*` names are macros that call the `fio_fx86*` functions; avoid side-effect-heavy arguments even when they appear to be evaluated once today.
# Static Scratch Allocator
A tiny, statically-backed allocator for short-lived temporary buffers.
Use it when you need a quick buffer you can return to a caller that should not be freed by the caller, or as a safer alternative to `alloca` for data that only needs to stay valid until the next few function calls.
The implementation is in [`./000 core.h`](https://facil.io/edge/api/000-core/).
## `FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`
```c
#ifndef FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX
#define FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX 256
#endif
```
Default slot count for library allocators that explicitly pass it as the fourth argument to `FIO_STATIC_ALLOC_DEF`.
`FIO_STATIC_ALLOC_DEF` does not use this setting implicitly. For each allocator, choose its `max_concurrent_allocations` argument according to the number of short-lived slots that may be outstanding at once. Raise this setting only when an allocator uses it and its slots are being reused too early.
## `FIO_STATIC_ALLOC_DEF`
```c
#define FIO_STATIC_ALLOC_DEF(name, type_T, units_per_allocation, max_concurrent_allocations)
```
Defines a static allocator named `name`.
```c
static type_T *name(size_t count);
```
The generated function returns an aligned `type_T *` to a slot containing `units_per_allocation` elements. Its `count` argument advances the atomic round-robin position by that number of slots; it does not change the size of the returned slot. Passing `0` returns the first slot without advancing the position.
- `name` — the allocator function name.
- `type_T` — the element type of each slot.
- `units_per_allocation` — number of `type_T` elements in each slot.
- `max_concurrent_allocations` — number of round-robin slots available before reuse.
The logical arena capacity reported by `name##_size()` is:
```
max_concurrent_allocations * units_per_allocation
```
## Generated API
The macro produces two functions:
- **`type_T *name(size_t count)`** — returns a pointer to a slot selected from the static round-robin buffer. No matching free exists; the memory is static and reused automatically.
- **`size_t name##_size(void)`** — returns the logical arena capacity in `type_T` units.
There is no generated `name##_free`, `name##_reset`, or similar helper. The buffer is managed by an atomic position counter and wraps around after `max_concurrent_allocations` slots.
## Example
```c
FIO_STATIC_ALLOC_DEF(numer2hex_allocator,
char,
19,
FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX);
char *ntos16(uint16_t n) {
char *buf = numer2hex_allocator(1);
buf[0] = '0';
buf[1] = 'x';
fio_ltoa16u(buf + 2, n, 16);
buf[18] = 0;
return buf;
}
```
The returned string is valid only until the allocator wraps around. A similar pattern is used by `fiobj_num2cstr` for temporary conversions.
## Thread Safety
The allocator is only "good enough" thread-safe. The atomic position counter protects the round-robin index, but the safety window is bounded by the allocator's `max_concurrent_allocations` argument. Keep slots short-lived and do not hold a returned pointer across too many calls or threads.
## `FIO_STATIC_SAFE_ALLOC_DEF`
```c
#define FIO_STATIC_SAFE_ALLOC_DEF(name, type_T, units_per_allocation, max_concurrent_allocations)
```
A contention-safe variant of `FIO_STATIC_ALLOC_DEF` for short-lived scratch slots with an explicit checkout lifecycle. Where the round-robin variant silently reuses a slot once more than `max_concurrent_allocations` are outstanding (corrupting the in-flight user), this allocator returns `NULL` when every slot is busy, letting callers fall back gracefully.
Each slot carries a small metadata header holding its busy byte. The header is `min(sizeof(type_T), 16)` bytes: one element for small types (data naturally aligned for `type_T`), capped at 16 bytes for larger types (data 16-byte aligned). `type_T` alignment must be ≤ 16 (enforced at compile time).
### Generated API
- **`type_T *name##_try(void)`** — checks out a slot and returns a pointer to its data block (right after the metadata header), or `NULL` when all slots are busy.
- **`void name##_free(type_T *ptr)`** — releases a slot returned by `name##_try` (clears the busy byte).
- **`size_t name##_size(void)`** — logical arena capacity in `type_T` units.
### Example
```c
FIO_STATIC_SAFE_ALLOC_DEF(my_scratch, uint8_t, 4096, 16);
size_t work(const void *in, size_t len) {
uint8_t *slot = my_scratch_try();
if (!slot)
return 0; /* contention: caller falls back (e.g. uncompressed) */
size_t r = do_work(slot, in, len);
my_scratch_free(slot);
return r;
}
```
### Thread Safety
Slot checkout uses an atomic busy byte per slot with an atomic round-robin start hint; a checked-out slot is exclusively owned until `name##_free`. The busy-byte protocol is exact (no safety window) — the only failure mode is `NULL` under contention, which callers must handle.
# String and Buffer Information
Lightweight descriptors for byte ranges. They are defined in [`./000 core.h`](https://facil.io/edge/api/000-core/) and are used throughout the library to pass around strings and buffers without taking ownership.
## Descriptor Types
### `fio_str_info_s`
A string descriptor that carries length, buffer pointer, and capacity.
```c
typedef struct fio_str_info_s {
size_t len; /* string length, in bytes */
char *buf; /* pointer to the first byte, or NULL on error */
size_t capa; /* buffer capacity; 0 means read-only */
} fio_str_info_s;
```
`capa` tells callers how much room is available in the underlying buffer. When `capa` is `0`, the buffer is considered read-only and must not be written to through the descriptor.
### `fio_buf_info_s`
A lighter buffer descriptor with no capacity field.
```c
typedef struct fio_buf_info_s {
size_t len; /* buffer length, in bytes */
char *buf; /* pointer to the first byte, may be NULL */
} fio_buf_info_s;
```
Use this when only a length/buffer pair is needed, such as for input data or temporary views.
## Construction Macros
### `FIO_STR_INFO0`
An all-zero `fio_str_info_s`.
```c
#define FIO_STR_INFO0 ((fio_str_info_s){0})
```
### `FIO_STR_INFO1(str)`
Build an `fio_str_info_s` from a NUL-terminated C string. Computes the length with `FIO_STRLEN`. If `str` is `NULL`, the length is `0`.
```c
#define FIO_STR_INFO1(str) \
((fio_str_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
### `FIO_STR_INFO2(str, length)`
Build an `fio_str_info_s` from a buffer and a known length.
```c
#define FIO_STR_INFO2(str, length) \
((fio_str_info_s){.len = (length), .buf = (str)})
```
### `FIO_STR_INFO3(str, length, capacity)`
Build a fully initialized `fio_str_info_s`.
```c
#define FIO_STR_INFO3(str, length, capacity) \
((fio_str_info_s){.len = (length), .buf = (str), .capa = (capacity)})
```
### `FIO_BUF_INFO0`
An all-zero `fio_buf_info_s`.
```c
#define FIO_BUF_INFO0 ((fio_buf_info_s){0})
```
### `FIO_BUF_INFO1(str)`
Build an `fio_buf_info_s` from a NUL-terminated C string. If `str` is `NULL`, the length is `0`.
```c
#define FIO_BUF_INFO1(str) \
((fio_buf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
### `FIO_BUF_INFO2(str, length)`
Build an `fio_buf_info_s` from a buffer and a known length.
```c
#define FIO_BUF_INFO2(str, length) \
((fio_buf_info_s){.len = (length), .buf = (str)})
```
## Equality
### `FIO_STR_INFO_IS_EQ(s1, s2)`
Returns a true value if two `fio_str_info_s` objects have the same content.
The comparison is a fast-path short-circuit: equal length and either both empty, the same pointer, or matching first byte and `FIO_MEMCMP` equal.
```c
#define FIO_STR_INFO_IS_EQ(s1, s2) \
((s1).len == (s2).len && \
(!(s1).len || (s1).buf == (s2).buf || \
((s1).buf && (s2).buf && (s1).buf[0] == (s2).buf[0] && \
!FIO_MEMCMP((s1).buf, (s2).buf, (s1).len))))
```
### `FIO_BUF_INFO_IS_EQ(s1, s2)`
Same comparison, routed to `FIO_STR_INFO_IS_EQ`.
```c
#define FIO_BUF_INFO_IS_EQ(s1, s2) FIO_STR_INFO_IS_EQ((s1), (s2))
```
## Conversion
### `FIO_BUF2STR_INFO(buf_info)`
Convert an `fio_buf_info_s` to an `fio_str_info_s`. The resulting descriptor has the same `len` and `buf`; `capa` is left as `0`.
```c
#define FIO_BUF2STR_INFO(buf_info) \
((fio_str_info_s){.len = (buf_info).len, .buf = (buf_info).buf})
```
### `FIO_STR2BUF_INFO(str_info)`
Convert an `fio_str_info_s` to an `fio_buf_info_s`, dropping the capacity.
```c
#define FIO_STR2BUF_INFO(str_info) \
((fio_buf_info_s){.len = (str_info).len, .buf = (str_info).buf})
```
## Temporary Stack Variables
### `FIO_STR_INFO_TMP_VAR(name, capacity)`
Creates a stack-allocated `fio_str_info_s` named `name` with `capacity` writable bytes. The macro allocates `capacity + 1` bytes so a trailing NUL guard is always present, and initializes `name.buf` and `name.capa`.
```c
#define FIO_STR_INFO_TMP_VAR(name, capacity) \
char fio___stack_mem___##name[(capacity) + 1]; \
fio___stack_mem___##name[0] = 0; /* guard */ \
fio_str_info_s name = (fio_str_info_s) { \
.buf = fio___stack_mem___##name, .capa = (capacity) \
}
```
Use this when a function needs a writable working buffer that normally fits on the stack but may be reallocated by downstream helpers.
### `FIO_STR_INFO_TMP_IS_REALLOCATED(name)`
Returns a true value if the temporary variable `name` no longer points to its original stack buffer, which indicates that a helper reallocated the memory onto the heap.
```c
#define FIO_STR_INFO_TMP_IS_REALLOCATED(name) \
(fio___stack_mem___##name != name.buf)
```
If this returns true, the buffer must eventually be freed by whatever allocator the helper used (usually the facil.io allocator or the override set via `FIO_MEM_REALLOC`).
# UTF-8 Helpers
Single-character encoding, decoding, and validation helpers for UTF-8 text. Defined in [`./000 core.h`](https://facil.io/edge/api/000-core/).
These functions work on one code point at a time and do not check memory bounds. Use them carefully — make sure the buffer has enough space for writes and is padded or NUL-terminated for reads.
## Compile-Time Flag
### `FIO_UTF8_ALLOW_IF`
Controls whether the UTF-8 helpers may use branchy (`if`) fast paths.
- **`1`** (default): ASCII-biased fast paths. Best when most input is ASCII.
- **`0`**: Branchless, constant-time-ish paths. Better when input distribution is unpredictable and branch mispredictions are a concern.
Defined before including [`./000 core.h`](https://facil.io/edge/api/000-core/) to override the default.
## Length Helpers
### `fio_utf8_code_len`
```c
unsigned fio_utf8_code_len(uint32_t u);
```
Returns the number of bytes required to encode code point `u` in UTF-8.
Returns `1`–`4` for code points up to `0x1FFFFF`, and returns `0` only above that bound.
This bound is above the valid Unicode range (`0x10FFFF`), so the function accepts code points in the range `0x110000`–`0x1FFFFF` as a consequence of its bitmask. `fio_utf8_write` will encode those code points as 4-byte sequences.
### `fio_utf8_char_len_unsafe`
```c
unsigned fio_utf8_char_len_unsafe(uint8_t c);
```
Classifies a single leading byte without validating continuation bytes.
Returns:
- `1`–`4`: the expected total byte length of the UTF-8 character.
- `8`: a continuation byte (middle of a multi-byte character).
- `0`: an invalid leading byte.
Use this only to recover length information after a successful `fio_utf8_char_len`, `fio_utf8_write`, or similar call where validity is already known.
### `fio_utf8_char_len`
```c
unsigned fio_utf8_char_len(const void *str);
```
Returns the number of valid UTF-8 bytes used by the first character at `str`, validating continuation bytes.
Returns `0` if `str` does not point to a valid UTF-8-encoded code point. Returns `1`–`4` for a valid character.
## Write Helper
### `fio_utf8_write`
```c
unsigned fio_utf8_write(void *dest, uint32_t u);
```
Writes the UTF-8 encoding of code point `u` to `dest`. Returns the number of bytes written (`0`–`4`).
`u` is treated as described for `fio_utf8_code_len`: code points up to `0x1FFFFF` are encoded (including the non-Unicode range `0x110000`–`0x1FFFFF`), and higher values result in `0` bytes written.
The caller must ensure `dest` has room for up to 4 bytes. Typical use advances the destination pointer:
```c
dest += fio_utf8_write(dest, u);
```
## Read Helper
### `fio_utf8_read`
```c
uint32_t fio_utf8_read(char **str);
```
Decodes the first UTF-8 character at `*str` and returns its code point. Advances `*str` by the number of bytes consumed.
Returns `0` if the byte at `*str` is not a valid UTF-8 start byte or continuation sequence.
## Peek Helper
### `fio_utf8_peek`
```c
uint32_t fio_utf8_peek(const char *str);
```
Decodes the first UTF-8 character at `str` and returns its code point. Does not modify `str`.
Returns `0` for invalid input, same as `fio_utf8_read`.
## See Also
- [`./000 core.md`](https://facil.io/edge/facil-io-stl-core/) — overview of core helpers.
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth for the UTF-8 helpers.
- [`./001 string info.md`](https://facil.io/edge/string-and-buffer-information/) — string and buffer descriptors used with text helpers.
# Vector Math
Wide integer unions and lane-wise arithmetic helpers defined in [`./000 core.h`](https://facil.io/edge/api/000-core/). These types are used for SIMD-style processing, big-integer arithmetic, hashing, and cryptography.
## Wide Union Types
These unions expose the same data through arrays of different widths, plus platform-specific vector views when SIMD is available. They are sized and aligned as follows:
| Type | Size | Alignment |
|------|------|-----------|
| `fio_u128` | 16 bytes | 16 |
| `fio_u256` | 32 bytes | 32 |
| `fio_u512` | 64 bytes | 64 |
| `fio_u1024` | 128 bytes | 64 |
| `fio_u2048` | 256 bytes | 64 |
| `fio_u4096` | 512 bytes | 64 |
All types include these overlapping members (counts vary by element width):
```c
size_t uz[N]; ssize_t iz[N];
uint64_t u64[N]; int64_t i64[N];
uint32_t u32[N]; int32_t i32[N];
uint16_t u16[N]; int16_t i16[N];
uint8_t u8[N]; int8_t i8[N];
float f[N];
double d[N];
long double ld[total_bytes / sizeof(long double)];
```
`total_bytes` is the type size from the table above, and each `N` is `total_bytes / sizeof(element)`. Larger types also contain smaller ones, for example `fio_u1024` provides `u128[8]`, `u256[4]`, and `u512[2]` in addition to the generic arrays.
When the compiler supports GCC/Clang `vector_size` attributes or ARM NEON intrinsics, the unions also expose vector views named `x64`, `x32`, `x16`, and `x8`. The internal layout adapts to the platform:
- On GCC/Clang, `x64[1]` is a single full-width vector.
- On ARM NEON, `x64[]` is an array of 128-bit `uint64x2_t` vectors.
- On platforms without vector support, `x64[]` falls back to a plain `uint64_t` array.
## Initialization Macros
Initialize a wide union from a list of lane values. Unused lanes are zeroed by the compiler.
```c
fio_u128_init8(0x01, 0x02, ...)
fio_u128_init16(...)
fio_u128_init32(...)
fio_u128_init64(...)
fio_u256_init8(...)
fio_u256_init16(...)
fio_u256_init32(...)
fio_u256_init64(...)
fio_u512_init8(...)
fio_u512_init16(...)
fio_u512_init32(...)
fio_u512_init64(...)
fio_u1024_init8(...)
fio_u1024_init16(...)
fio_u1024_init32(...)
fio_u1024_init64(...)
fio_u2048_init8(...)
fio_u2048_init16(...)
fio_u2048_init32(...)
fio_u2048_init64(...)
fio_u4096_init8(...)
fio_u4096_init16(...)
fio_u4096_init32(...)
fio_u4096_init64(...)
```
## Load, Store, and Byte Swap
All loads and stores copy whole vectors to and from memory using the fixed-size `fio_memcpyNN` helpers.
```c
fio_u128 fio_u128_load(const void *buf);
void fio_u128_store(void *buf, const fio_u128 a);
fio_u256 fio_u256_load(const void *buf);
void fio_u256_store(void *buf, const fio_u256 a);
fio_u512 fio_u512_load(const void *buf);
void fio_u512_store(void *buf, const fio_u512 a);
fio_u1024 fio_u1024_load(const void *buf);
void fio_u1024_store(void *buf, const fio_u1024 a);
fio_u2048 fio_u2048_load(const void *buf);
void fio_u2048_store(void *buf, const fio_u2048 a);
fio_u4096 fio_u4096_load(const void *buf);
void fio_u4096_store(void *buf, const fio_u4096 a);
```
Endian-aware load helpers read each lane from the given byte order and convert it to local order. Store variants are not provided here; use the byte-swap helpers before `store` if needed.
```c
fio_u128 fio_u128_load_le16(const void *buf);
fio_u128 fio_u128_load_be16(const void *buf);
fio_u128 fio_u128_load_le32(const void *buf);
fio_u128 fio_u128_load_be32(const void *buf);
fio_u128 fio_u128_load_le64(const void *buf);
fio_u128 fio_u128_load_be64(const void *buf);
fio_u256 fio_u256_load_le16(...); fio_u256 fio_u256_load_be16(...);
fio_u256 fio_u256_load_le32(...); fio_u256 fio_u256_load_be32(...);
fio_u256 fio_u256_load_le64(...); fio_u256 fio_u256_load_be64(...);
fio_u512 fio_u512_load_le16(...); fio_u512 fio_u512_load_be16(...);
fio_u512 fio_u512_load_le32(...); fio_u512 fio_u512_load_be32(...);
fio_u512 fio_u512_load_le64(...); fio_u512 fio_u512_load_be64(...);
fio_u1024 fio_u1024_load_le16(...); fio_u1024 fio_u1024_load_be16(...);
fio_u1024 fio_u1024_load_le32(...); fio_u1024 fio_u1024_load_be32(...);
fio_u1024 fio_u1024_load_le64(...); fio_u1024 fio_u1024_load_be64(...);
fio_u2048 fio_u2048_load_le16(...); fio_u2048 fio_u2048_load_be16(...);
fio_u2048 fio_u2048_load_le32(...); fio_u2048 fio_u2048_load_be32(...);
fio_u2048 fio_u2048_load_le64(...); fio_u2048 fio_u2048_load_be64(...);
fio_u4096 fio_u4096_load_le16(...); fio_u4096 fio_u4096_load_be16(...);
fio_u4096 fio_u4096_load_le32(...); fio_u4096 fio_u4096_load_be32(...);
fio_u4096 fio_u4096_load_le64(...); fio_u4096 fio_u4096_load_be64(...);
```
Byte-swap every lane of a vector, returning the result by value:
```c
fio_u128 fio_u128_bswap16(fio_u128 a);
fio_u128 fio_u128_bswap32(fio_u128 a);
fio_u128 fio_u128_bswap64(fio_u128 a);
fio_u256 fio_u256_bswap16(...); fio_u256 fio_u256_bswap32(...); fio_u256 fio_u256_bswap64(...);
fio_u512 fio_u512_bswap16(...); fio_u512 fio_u512_bswap32(...); fio_u512 fio_u512_bswap64(...);
fio_u1024 fio_u1024_bswap16(...); fio_u1024 fio_u1024_bswap32(...); fio_u1024 fio_u1024_bswap64(...);
fio_u2048 fio_u2048_bswap16(...); fio_u2048 fio_u2048_bswap32(...); fio_u2048 fio_u2048_bswap64(...);
fio_u4096 fio_u4096_bswap16(...); fio_u4096 fio_u4096_bswap32(...); fio_u4096 fio_u4096_bswap64(...);
```
## Lane-Wise Arithmetic
The arithmetic functions write lane-wise results into `*target`. They work on any `fio_uXXX` type at 8, 16, 32, and 64-bit lane widths. The compiler vectorizes the loops when SIMD is available.
```c
void fio_u128_add8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_add64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_sub8(...); void fio_u128_sub16(...); void fio_u128_sub32(...); void fio_u128_sub64(...);
void fio_u128_mul8(...); void fio_u128_mul16(...); void fio_u128_mul32(...); void fio_u128_mul64(...);
void fio_u128_and8(...); void fio_u128_and16(...); void fio_u128_and32(...); void fio_u128_and64(...);
void fio_u128_or8(...); void fio_u128_or16(...); void fio_u128_or32(...); void fio_u128_or64(...);
void fio_u128_xor8(...); void fio_u128_xor16(...); void fio_u128_xor32(...); void fio_u128_xor64(...);
```
These are generated for all wide types: `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
Constant-operand variants apply the same operation with a scalar in place of `*b`:
```c
void fio_u128_cadd8(fio_u128 *target, const fio_u128 *a, uint8_t b);
void fio_u128_cadd16(fio_u128 *target, const fio_u128 *a, uint16_t b);
void fio_u128_cadd32(fio_u128 *target, const fio_u128 *a, uint32_t b);
void fio_u128_cadd64(fio_u128 *target, const fio_u128 *a, uint64_t b);
void fio_u128_csub8(...); void fio_u128_csub16(...); void fio_u128_csub32(...); void fio_u128_csub64(...);
void fio_u128_cmul8(...); void fio_u128_cmul16(...); void fio_u128_cmul32(...); void fio_u128_cmul64(...);
void fio_u128_cand8(...); void fio_u128_cand16(...); void fio_u128_cand32(...); void fio_u128_cand64(...);
void fio_u128_cor8(...); void fio_u128_cor16(...); void fio_u128_cor32(...); void fio_u128_cor64(...);
void fio_u128_cxor8(...); void fio_u128_cxor16(...); void fio_u128_cxor32(...); void fio_u128_cxor64(...);
```
Reduction functions fold all lanes of the given width into a single scalar:
```c
uint8_t fio_u128_reduce_add8(const fio_u128 *a);
uint16_t fio_u128_reduce_add16(const fio_u128 *a);
uint32_t fio_u128_reduce_add32(const fio_u128 *a);
uint64_t fio_u128_reduce_add64(const fio_u128 *a);
uint8_t fio_u128_reduce_sub8(...); uint16_t fio_u128_reduce_sub16(...);
uint32_t fio_u128_reduce_sub32(...); uint64_t fio_u128_reduce_sub64(...);
uint8_t fio_u128_reduce_mul8(...); uint16_t fio_u128_reduce_mul16(...);
uint32_t fio_u128_reduce_mul32(...); uint64_t fio_u128_reduce_mul64(...);
uint8_t fio_u128_reduce_and8(...); uint16_t fio_u128_reduce_and16(...);
uint32_t fio_u128_reduce_and32(...); uint64_t fio_u128_reduce_and64(...);
uint8_t fio_u128_reduce_or8(...); uint16_t fio_u128_reduce_or16(...);
uint32_t fio_u128_reduce_or32(...); uint64_t fio_u128_reduce_or64(...);
uint8_t fio_u128_reduce_xor8(...); uint16_t fio_u128_reduce_xor16(...);
uint32_t fio_u128_reduce_xor32(...); uint64_t fio_u128_reduce_xor64(...);
```
Whole-vector variants that operate on 64-bit lanes without a width suffix are also provided for bitwise operations:
```c
void fio_u128_and(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_or (fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
void fio_u128_xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b);
```
## Rotations
Lane-wise right and left rotations. Variable rotations read the per-lane shift from an array; constant rotations use a single `uint8_t` for all lanes.
```c
void fio_u128_rrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t rotations[16]);
void fio_u128_rrot16(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[8]);
void fio_u128_rrot32(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[4]);
void fio_u128_rrot64(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[2]);
void fio_u128_lrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t rotations[16]);
void fio_u128_lrot16(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[8]);
void fio_u128_lrot32(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[4]);
void fio_u128_lrot64(fio_u128 *target, const fio_u128 *a, const uint8_t rotations[2]);
void fio_u128_crrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_crrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot8 (fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
void fio_u128_clrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts);
```
All rotation families exist for `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096` with appropriate array sizes.
## Ternary Lane-Wise Operations
These compute common three-input bit functions across each lane.
```c
// target[i] = z[i] ^ (x[i] & (y[i] ^ z[i]))
void fio_u128_mux32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_mux64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
// target[i] = (x[i] & y[i]) | (z[i] & (x[i] | y[i]))
void fio_u128_maj32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_maj64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
// target[i] = x[i] ^ y[i] ^ z[i]
void fio_u128_3xor32(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_3xor64(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
```
Whole-vector 64-bit variants without a width suffix are also provided:
```c
void fio_u128_mux(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_maj(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
void fio_u128_3xor(fio_u128 *target, const fio_u128 *x, const fio_u128 *y, const fio_u128 *z);
```
All ternary functions exist for the other wide types: `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
## Whole-Vector Value Operations
A small set of convenience functions return results by value instead of writing through a pointer. They operate on 64-bit lanes and are designed to let the compiler use SIMD directly.
```c
fio_u128 fio_u128_xorv(fio_u128 a, fio_u128 b);
fio_u128 fio_u128_andv(fio_u128 a, fio_u128 b);
fio_u128 fio_u128_orv (fio_u128 a, fio_u128 b);
fio_u128 fio_u128_addv64(fio_u128 a, fio_u128 b);
fio_u256 fio_u256_xorv(...); fio_u256 fio_u256_andv(...); fio_u256 fio_u256_orv(...); fio_u256 fio_u256_addv64(...);
fio_u512 fio_u512_xorv(...); fio_u512 fio_u512_andv(...); fio_u512 fio_u512_orv(...); fio_u512 fio_u512_addv64(...);
fio_u1024 fio_u1024_xorv(...); fio_u1024 fio_u1024_andv(...); fio_u1024 fio_u1024_orv(...); fio_u1024 fio_u1024_addv64(...);
fio_u2048 fio_u2048_xorv(...); fio_u2048 fio_u2048_andv(...); fio_u2048 fio_u2048_orv(...); fio_u2048 fio_u2048_addv64(...);
fio_u4096 fio_u4096_xorv(...); fio_u4096 fio_u4096_andv(...); fio_u4096 fio_u4096_orv(...); fio_u4096 fio_u4096_addv64(...);
```
## Constant-Time Helpers
```c
bool fio_u128_is_eq(const fio_u128 *a, const fio_u128 *b);
```
Returns `true` if every byte of `*a` equals the corresponding byte of `*b`, otherwise `false`. The comparison is branchless over the vector body.
```c
void fio_u128_inv(fio_u128 *target, const fio_u128 *a);
```
Bitwise NOT: `target[i] = ~a[i]` on 64-bit lanes.
```c
void fio_u128_ct_swap_if(bool cond, fio_u128 *a, fio_u128 *b);
```
Swaps `*a` and `*b` when `cond` is true, without branching on `cond`. Uses a mask derived from `(uint64_t)0 - cond`.
All three helpers exist for `fio_u256`, `fio_u512`, `fio_u1024`, `fio_u2048`, and `fio_u4096`.
## Multi-Precision Math
These functions treat the wide unions as unsigned big integers made of little-endian `u64` words.
```c
uint64_t fio_u128_add(fio_u128 *result, const fio_u128 *a, const fio_u128 *b);
uint64_t fio_u128_sub(fio_u128 *result, const fio_u128 *a, const fio_u128 *b);
int fio_u128_cmp(const fio_u128 *a, const fio_u128 *b);
uint64_t fio_u256_add(...); uint64_t fio_u256_sub(...); int fio_u256_cmp(...);
uint64_t fio_u512_add(...); uint64_t fio_u512_sub(...); int fio_u512_cmp(...);
uint64_t fio_u1024_add(...); uint64_t fio_u1024_sub(...); int fio_u1024_cmp(...);
uint64_t fio_u2048_add(...); uint64_t fio_u2048_sub(...); int fio_u2048_cmp(...);
uint64_t fio_u4096_add(...); uint64_t fio_u4096_sub(...); int fio_u4096_cmp(...);
```
- `fio_uXXX_add` returns the carry bit (`1` or `0`).
- `fio_uXXX_sub` returns the borrow bit (`1` or `0`).
- `fio_uXXX_cmp` returns `-1` if `a < b`, `0` if equal, and `1` if `a > b`.
Multiplication doubles the width of the result:
```c
void fio_u128_mul(fio_u256 *result, const fio_u128 *a, const fio_u128 *b);
void fio_u256_mul(fio_u512 *result, const fio_u256 *a, const fio_u256 *b);
void fio_u512_mul(fio_u1024 *result, const fio_u512 *a, const fio_u512 *b);
void fio_u1024_mul(fio_u2048 *result, const fio_u1024 *a, const fio_u1024 *b);
void fio_u2048_mul(fio_u4096 *result, const fio_u2048 *a, const fio_u2048 *b);
```
Montgomery multiplication is provided for RSA-like modular arithmetic:
```c
void fio_u128_montgomery_mul(fio_u128 *result, const fio_u128 *a, const fio_u128 *b,
const fio_u128 *N, const fio_u128 *N_dash);
void fio_u256_montgomery_mul(fio_u256 *result, const fio_u256 *a, const fio_u256 *b,
const fio_u256 *N, const fio_u256 *N_dash);
void fio_u512_montgomery_mul(fio_u512 *result, const fio_u512 *a, const fio_u512 *b,
const fio_u512 *N, const fio_u512 *N_dash);
void fio_u1024_montgomery_mul(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b,
const fio_u1024 *N, const fio_u1024 *N_dash);
void fio_u2048_montgomery_mul(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b,
const fio_u2048 *N, const fio_u2048 *N_dash);
```
The multiplication uses `fio_math_mul` from [`./002 math.h`](https://facil.io/edge/api/002-math/).
## Shuffle and Reduce on Native Arrays
A separate family of helpers works on plain C arrays of fixed size. They are useful when the data is not wrapped in a `fio_uXXX` union.
### Shuffle
```c
void fio_u8x4_reshuffle (uint8_t *v, uint8_t indx[4]);
void fio_u8x8_reshuffle (uint8_t *v, uint8_t indx[8]);
void fio_u8x16_reshuffle (uint8_t *v, uint8_t indx[16]);
void fio_u8x32_reshuffle (uint8_t *v, uint8_t indx[32]);
void fio_u8x64_reshuffle (uint8_t *v, uint8_t indx[64]);
void fio_u8x128_reshuffle(uint8_t *v, uint8_t indx[128]);
void fio_u8x256_reshuffle(uint8_t *v, uint8_t indx[256]);
void fio_u16x2_reshuffle (uint16_t *v, uint8_t indx[2]);
void fio_u16x4_reshuffle (uint16_t *v, uint8_t indx[4]);
void fio_u16x8_reshuffle (uint16_t *v, uint8_t indx[8]);
void fio_u16x16_reshuffle(uint16_t *v, uint8_t indx[16]);
void fio_u16x32_reshuffle(uint16_t *v, uint8_t indx[32]);
void fio_u16x64_reshuffle(uint16_t *v, uint8_t indx[64]);
void fio_u16x128_reshuffle(uint16_t *v, uint8_t indx[128]);
void fio_u32x2_reshuffle (uint32_t *v, uint8_t indx[2]);
void fio_u32x4_reshuffle (uint32_t *v, uint8_t indx[4]);
void fio_u32x8_reshuffle (uint32_t *v, uint8_t indx[8]);
void fio_u32x16_reshuffle(uint32_t *v, uint8_t indx[16]);
void fio_u32x32_reshuffle(uint32_t *v, uint8_t indx[32]);
void fio_u32x64_reshuffle(uint32_t *v, uint8_t indx[64]);
void fio_u64x2_reshuffle (uint64_t *v, uint8_t indx[2]);
void fio_u64x4_reshuffle (uint64_t *v, uint8_t indx[4]);
void fio_u64x8_reshuffle (uint64_t *v, uint8_t indx[8]);
void fio_u64x16_reshuffle(uint64_t *v, uint8_t indx[16]);
void fio_u64x32_reshuffle(uint64_t *v, uint8_t indx[32]);
void fio_floatx2_reshuffle (float *v, uint8_t indx[2]);
void fio_floatx4_reshuffle (float *v, uint8_t indx[4]);
void fio_floatx8_reshuffle (float *v, uint8_t indx[8]);
void fio_floatx16_reshuffle(float *v, uint8_t indx[16]);
void fio_floatx32_reshuffle(float *v, uint8_t indx[32]);
void fio_floatx64_reshuffle(float *v, uint8_t indx[64]);
void fio_dblx2_reshuffle (double *v, uint8_t indx[2]);
void fio_dblx4_reshuffle (double *v, uint8_t indx[4]);
void fio_dblx8_reshuffle (double *v, uint8_t indx[8]);
void fio_dblx16_reshuffle (double *v, uint8_t indx[16]);
void fio_dblx32_reshuffle (double *v, uint8_t indx[32]);
```
Indices are masked with `len - 1`, so out-of-range indices wrap safely.
Variadic macro wrappers let you pass the index list inline:
```c
fio_u8x4_reshuffle(v, 3, 2, 1, 0);
fio_u32x4_reshuffle(v, 0, 2, 1, 3);
```
### Reduce
Integer reductions provide add, subtract, multiply, bitwise and/or/xor, and min/max:
```c
uint8_t fio_u8x4_reduce_add (uint8_t *v); uint8_t fio_u8x4_reduce_sub (uint8_t *v);
uint8_t fio_u8x4_reduce_mul (uint8_t *v); uint8_t fio_u8x4_reduce_and (uint8_t *v);
uint8_t fio_u8x4_reduce_or (uint8_t *v); uint8_t fio_u8x4_reduce_xor (uint8_t *v);
uint8_t fio_u8x4_reduce_max (uint8_t *v); uint8_t fio_u8x4_reduce_min (uint8_t *v);
// Same pattern for u8 x 8, 16, 32, 64, 128, 256
// Same pattern for u16 x 2, 4, 8, 16, 32, 64, 128
// Same pattern for u32 x 2, 4, 8, 16, 32, 64
// Same pattern for u64 x 2, 4, 8, 16, 32
```
Floating-point reductions provide add, multiply, and min/max (no bitwise operations):
```c
float fio_floatx2_reduce_add(float *v); float fio_floatx2_reduce_mul(float *v);
float fio_floatx2_reduce_max(float *v); float fio_floatx2_reduce_min(float *v);
// Same pattern for float x 4, 8, 16, 32, 64
// Same pattern for dbl x 2, 4, 8, 16, 32
```
### Lane-wise operations on native arrays
Each integer reduction family also defines matching in-place binary operators on the arrays themselves:
```c
void fio_u8x4_add (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_sub (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_mul (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_and (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_or (uint8_t *dest, uint8_t *a, uint8_t *b);
void fio_u8x4_xor (uint8_t *dest, uint8_t *a, uint8_t *b);
// Same pattern for all u8/u16/u32/u64 widths and lengths.
```
Floating-point array families define add and multiply only:
```c
void fio_floatx2_add(float *dest, float *a, float *b);
void fio_floatx2_mul(float *dest, float *a, float *b);
// Same pattern for float x 4, 8, 16, 32, 64 and dbl x 2, 4, 8, 16, 32.
```
## See Also
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — source of truth for all vector definitions.
- [`./000 core.md`](https://facil.io/edge/facil-io-stl-core/) — core documentation index.
- `001 fx86.h` — fake/portable x86 intrinsics built on these types.
- [`./002 math.h`](https://facil.io/edge/api/002-math/) — multi-precision `fio_math_mul` and related helpers.
# Version and Settings
The knobs that control how facil.io identifies itself and behaves at compile time.
For core types and everyday helpers, see [`./000 core.md`](https://facil.io/edge/facil-io-stl-core/). This page covers version macros and core behavioral defaults; for per-inclusion hooks such as allocator routing and pointer tagging, see [`./001 header.h`](https://facil.io/edge/api/001-header/).
## Version Macros
facil.io follows [semantic versioning](https://semver.org). These macros are defined in [`./000 core.h`](https://facil.io/edge/api/000-core/) and can be used to detect the library version at compile time.
- **`FIO_VERSION_MAJOR`** — Major version. API/ABI breaking changes bump this.
Default: `0`
- **`FIO_VERSION_MINOR`** — Minor version. Significant features or deprecations. May break ABI.
Default: `8`
- **`FIO_VERSION_PATCH`** — Patch version. Bug fixes and small additions.
Default: `0`
- **`FIO_VERSION_BUILD`** — Optional build metadata, such as `"rc.02"`.
Default: `"rc.02"`
- **`FIO_VERSION_STRING`** — Full version as a string literal, built from the macros above. The guard is `#ifdef FIO_VERSION_BUILD`, so the build metadata is included whenever `FIO_VERSION_BUILD` is defined, including when defined as an empty string: `"0.8.0-rc.02"`. If `FIO_VERSION_BUILD` is undefined, the string reads `"0.8.0"`.
Because [`./000 core.h`](https://facil.io/edge/api/000-core/) unconditionally `#define`s `FIO_VERSION_BUILD "rc.02"` before the `#ifdef` guard, omitting the suffix requires `#undef FIO_VERSION_BUILD` before inclusion, not merely leaving it undefined.
Override these only when you are vendoring a fork or need to report a custom build string.
## Compile-Time Behavioral Defaults
Most defaults are guarded with `#ifndef`; define them before including the header to override the default. `FIO_NO_LOG` and `DEBUG` are presence flags rather than `#ifndef` defaults.
### Memory and Safety
- **`FIO_LEAK_COUNTER`** — Enables memory-leak counting. When enabled, the leak counter tracks allocations made through facil.io allocators and prints a summary at exit.
Default: `1` (enabled).
Set to `0` to disable counting and the exit report.
Note: this conflicts with `FIO_NO_LOG`, because the leak report uses the logging layer.
- **`FIO_LEAK_COUNTER_SKIP_EXIT`** — Skip the automatic leak report at exit when leak counting is enabled.
Default: `0` (report at exit).
- **`FIO_LEAK_COUNTER_DEF(name)`** — Declares a named leak counter.
- **`FIO_LEAK_COUNTER_ON_ALLOC(name)`** — Records an allocation against the named counter.
- **`FIO_LEAK_COUNTER_ON_FREE(name)`** — Records a free against the named counter.
- **`FIO_LEAK_COUNTER_COUNT(name)`** — Returns the current value of the named counter.
- **`FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`** — When non-zero, facil.io allocators zero-initialize returned memory by default.
Default: `1` (secure by default).
Set to `0` if you want raw allocations and prefer to do your own initialization.
- **`FIO_MEM_PAGE_SIZE_LOG`** — Log₂ of the OS memory page size. The memory allocator uses this to round allocations and map memory efficiently.
Default: `12` (4096-byte pages).
Change it if you are targeting a system with a different page size.
- **`FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`** — Default slot count for selected internal `FIO_STATIC_ALLOC_DEF` invocations (default: `256`). The macro does not use this setting implicitly; pass it explicitly as the fourth argument where appropriate.
### Concurrency
- **`FIO_USE_THREAD_MUTEX`** — Choose the locking primitive used by thread-safe modules.
Default: `0` (use facil.io spinlocks).
Set to `1` to use OS native mutexes (`pthread_mutex_t` on POSIX) instead.
If you need this decision to apply to only a single `#include`, use `FIO_USE_THREAD_MUTEX_TMP` in [`./001 header.h`](https://facil.io/edge/api/001-header/) instead.
### Performance and Alignment
- **`FIO_UNALIGNED_ACCESS`** — Allows facil.io to attempt unaligned memory access on CPUs that support it. When enabled, the library may use faster paths in helpers such as `fio_buf2uXX`.
Default: `1` (attempt detection).
Set to `0` on strict-alignment architectures or if you want to forbid unaligned access entirely. The actual capability is reflected by `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED`, which is derived at compile time.
- **`FIO_LIMIT_INTRINSIC_BUFFER`** — Limits register pressure in some pseudo-intrinsic loops by processing smaller batches and looping more.
Default: `1`.
Set to `0` if you prefer larger unrolled buffers and do not mind the extra register use.
### Maps
- **`FIO_MAP_WARNING_BITSIZE`** — Bit size at which hash-map allocations trigger an internal warning threshold in the `imap`/`map` templates.
Default: `24`.
Raise or lower it to tune when the library starts worrying about map size during growth.
### Logging
- **`FIO_NO_LOG`** — Define this to remove all logging output. Cannot be combined with `FIO_LEAK_COUNTER`; the two are mutually exclusive.
- **`FIO_LOG2STDERR`** — Low-level sink used by the logging macros.
Default: no-op.
Override to print to `stderr` or route messages to a custom destination.
- **`FIO_LOG_LENGTH_LIMIT`** — Approximate cap on a single formatted log message, limiting stack memory use.
Default: `1024`.
- **`DEBUG`** — Define this to enable debug-only log macros (`FIO_LOG_D*`) and active `FIO_ASSERT_DEBUG` checks. Also disables SIMD/Neon intrinsics in [`./000 core.h`](https://facil.io/edge/api/000-core/).
### Memory allocator routing
These macros are defined in [`./001 header.h`](https://facil.io/edge/api/001-header/) and are re-evaluated on every inclusion cycle. Define them before the first fio-stl include to replace memory routing globally.
- **`FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len)`** — Reallocate a block, copying at least `copy_len` bytes if a move is required. Defaults to the system `realloc` unless the facil.io allocator is available, in which case it routes to `fio_realloc2`. Override to plug in a custom allocator.
- **`FIO_MEM_FREE(ptr, size)`** — Free a block. Defaults to the system `free` or `fio_free`. Override alongside `FIO_MEM_REALLOC`.
- **`FIO_MEMORY_DISABLE`** — Define to disable all custom facil.io allocators and force system `malloc`/`free` for temporary allocations.
- **`FIO_MALLOC_TMP_USE_SYSTEM`** — Force the recursive allocator variant to use the system `realloc`/`free`. Useful when a custom `FIO_MEM_REALLOC` needs an allocation path that will not recurse back into facil.io.
- **`FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`** — Recursive-safe copies of `FIO_MEM_REALLOC`/`FIO_MEM_FREE`. Override these when the custom allocator itself needs to allocate or free memory without re-entering the override.
- **`FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment)`** — Reallocate with a minimum pointer alignment, **assigning** the result to `ptr` (save a copy first if failure must be recoverable). `alignment` of `0` selects the allocator default; non power-of-2 values are rounded down to a power of 2; effective alignment never drops below the current alignment of `ptr` nor below the allocator default. Routes to `fio_realloc_aligned` when the facil.io allocator is available, or to the system backends (`_aligned_realloc` on Windows, `posix_memalign`-based emulation on POSIX).
- **`FIO_MEM_FREE_ALIGNED(ptr, size)`** — Free a block obtained through `FIO_MEM_REALLOC_ALIGNED`. With the facil.io allocator this is simply `fio_free` (which accepts interior pointers); with the system backends it routes to `_aligned_free` on Windows or `free` on POSIX. Custom allocators that stash alignment bookkeeping near the returned pointer should override this pair together.
- **`FIO_MEM_ALLOC_SIZE(size)`** — The usable bytes the allocator reserves for a `size` request (`fio_alloc_size`, or the identity function with the system allocator).
- **`FIO_MEM_REALLOC_ALIGNED_` / `FIO_MEM_FREE_ALIGNED_`** — Recursive-safe copies of the aligned pair, mirroring `FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`.
### Pointer tagging
These macros are defined in [`./001 header.h`](https://facil.io/edge/api/001-header/) and let included types store a small marker in the unused low bits of aligned pointers.
- **`FIO_PTR_TAG(p)`** — Apply the tag to a pointer. Default is the identity function.
- **`FIO_PTR_UNTAG(p)`** — Remove the tag from a pointer. Default is the identity function.
- **`FIO_PTR_TAG_TYPE`** — If defined, functions returning a type's pointer return this type instead.
- **`FIO_PTR_TAG_VALIDATE(ptr)`** — Returns a true value if the tagged pointer is valid. Default checks for non-`NULL`.
- **`FIO_PTR_TAG_VALID_OR_RETURN(tagged_ptr, value)`** — Validates the tagged pointer and returns `value` if it fails validation.
- **`FIO_PTR_TAG_VALID_OR_RETURN_VOID(tagged_ptr)`** — Validates the tagged pointer and returns `void` if it fails validation.
- **`FIO_PTR_TAG_VALID_OR_GOTO(tagged_ptr, label)`** — Validates the tagged pointer and jumps to `label` if it fails validation.
- **`FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr)`** — Cast the untagged pointer to `untagged_type *`.
Pointer tagging is used by dynamic-type wrappers (for example, FIOBJ) to distinguish object kinds without a separate type field.
## See Also
- [`./000 core.md`](https://facil.io/edge/facil-io-stl-core/) — documentation for core types and helpers.
- [`./000 core.h`](https://facil.io/edge/api/000-core/) — the source of truth for version macros and the defaults above.
- [`./001 header.h`](https://facil.io/edge/api/001-header/) — per-inclusion overrides such as `FIO_USE_THREAD_MUTEX_TMP` and allocator routing macros.
# String / Number Conversion
```c
#define FIO_ATOL
#include "fio-stl.h"
```
String-to-number and number-to-string helpers. These are the grunts that parse integers, floats, hex, binary, and arbitrary bases, then turn them back into text. Fast, greedy, and mostly guard-less — give them a valid buffer and a terminating character.
**Note:** functions that write to a buffer also write a NUL terminator. `fio_atol*` functions assume the buffer ends with an invalid character (such as NUL) and that allocations are aligned enough for multi-byte reads.
**Guard-byte contract:** parsing may read up to - and including - the first non-numeric byte following a number. Callers MUST keep the buffer readable through that byte: pass a NUL-terminated string or append a non-numeric guard byte. `fio_bstr` strings always satisfy this contract. Exact-size buffers with no readable byte past the number (e.g., a 1-byte allocation holding `"1"`) violate it and will trigger AddressSanitizer (sanitize with `FIO_MEMORY_DISABLE` for visibility).
### Configuration Macros
#### `FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER`
```c
#define FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER 1
```
When `1` (default), underscores act as digit separators: `1_000_000` parses as `1000000`. Set to `0` to disable.
### Types
#### `fio_aton_s`
```c
typedef struct {
union {
int64_t i;
double f;
uint64_t u;
};
int is_float;
int err;
} fio_aton_s;
```
Result container for `fio_aton`. Read the union member that matches `is_float`, and check `err` for overflow or parse failures.
### Universal Parsing
#### `fio_aton`
```c
FIO_SFUNC fio_aton_s fio_aton(char **pstr);
```
Auto-detects integers and floats. Skips leading whitespace, recognizes `0x` / `0b` / octal prefixes, and accepts `inf`, `infinity`, and `nan`. Updates `*pstr` to the first unconverted character. Sets `.err` on overflow or bad format.
**Note:** not an exact `strtod` replacement; rounding differences are possible.
### Strict, Bounded Parsing (wire-safe)
The `fio_stol*` family parses within an explicit `[pos, end)` window and accepts **only** the wire format: no whitespace, no underscores, no `0x`/`0b` prefixes, no `inf`/`nan`. Use these for network protocols and any length/size field - the lenient `fio_atol*` semantics (e.g., `1_0` parsing as `10`) are a differential-parsing hazard when a peer or intermediary parses the same field strictly.
All three advance `*pos` past the digits consumed (detect trailing junk or empty input by comparing `*pos` against expectations) and set `errno == E2BIG` on overflow, stopping at the last valid digit. Since the window is explicit, the guard-byte contract of the lenient family does not apply.
#### `fio_stol10u`
```c
SFUNC uint64_t fio_stol10u(char **pos, const char *end);
```
Parses an unsigned `uint64_t`, decimal digits only. No sign, no whitespace skip.
#### `fio_stol10`
```c
SFUNC int64_t fio_stol10(char **pos, const char *end);
```
Parses a signed `int64_t`: one optional leading `-` / `+`, then decimal digits only. `E2BIG` is set when the magnitude overflows `uint64_t` or exceeds the `int64_t` range (note: `-9223372036854775808` is exactly `INT64_MIN` and is **not** an overflow).
#### `fio_stol16u`
```c
SFUNC uint64_t fio_stol16u(char **pos, const char *end);
```
Parses an unsigned `uint64_t`, hex digits only - unlike `fio_atol16u`, a `0x` prefix is **not** consumed.
### Signed Conversion
#### `fio_atol`
```c
SFUNC int64_t fio_atol(char **pstr);
```
Parses a signed `int64_t`. Accepts base 10, octal (`0...`), hex (`0x...` or `x...`), and binary (`0b...` or `b...`). Updates `*pstr` past the number.
#### `fio_atof`
```c
SFUNC double fio_atof(char **pstr);
```
Parses a double. Wraps `strtod` for most inputs. The source also attempts to accept a raw `0b...` binary bit-pattern, but the detection condition looks fragile.
#### `fio_ftoa`
```c
SFUNC size_t fio_ftoa(char *dest, double num, uint8_t base);
```
Writes `num` to `dest` in `base` (2, 10, or 16; unsupported bases silently fall back to 10). No prefixes are added. Returns bytes written excluding NUL.
**Note:** provide at least 130 bytes for base 2. Special values `inf` and `nan` produce `"Infinity"` / `"NaN"`.
#### `fio_ltoa`
```c
SFUNC size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
```
Writes `num` to `dest` in `base` (2, 8, 10, 16, or any base up to 36). Adds `0x`, `0b`, or `0` prefixes for the built-in bases. Returns bytes written excluding NUL. If `dest` is `NULL`, writes to an internal scratch buffer and still returns the length. Logs an error and returns `0` for unsupported bases.
**Note:** provide at least 68 bytes for base 2.
#### `fio_ltoa10`
```c
FIO_IFUNC void fio_ltoa10(char *dest, int64_t i, size_t digits);
```
Writes a signed base-10 number using exactly `digits` bytes plus NUL. Use `fio_digits10()` to compute `digits`.
#### `fio_atol10`
```c
SFUNC int64_t fio_atol10(char **pstr);
```
Reads a signed base-10 number.
### Unsigned Conversion
#### `fio_atol8u`
```c
SFUNC uint64_t fio_atol8u(char **pstr);
```
Reads an unsigned octal number. May overflow the buffer if no terminator is present.
#### `fio_atol10u`
```c
SFUNC uint64_t fio_atol10u(char **pstr);
```
Reads an unsigned base-10 number.
#### `fio_atol16u`
```c
SFUNC uint64_t fio_atol16u(char **pstr);
```
Reads an unsigned hex number, with optional `0x` prefix.
#### `fio_atol_bin`
```c
SFUNC uint64_t fio_atol_bin(char **pstr);
```
Reads an unsigned binary number, with optional `0b` prefix.
#### `fio_atol_xbase`
```c
SFUNC uint64_t fio_atol_xbase(char **pstr, size_t base);
```
Reads an unsigned number in any base up to 36.
#### `fio_ltoa8u`
```c
FIO_IFUNC void fio_ltoa8u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned octal number using `digits` bytes plus NUL.
#### `fio_ltoa10u`
```c
FIO_IFUNC void fio_ltoa10u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned base-10 number using `digits` bytes plus NUL.
#### `fio_ltoa16u`
```c
FIO_IFUNC void fio_ltoa16u(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned hex number using `digits` bytes plus NUL. `digits` is rounded up to an even number.
#### `fio_ltoa_bin`
```c
FIO_IFUNC void fio_ltoa_bin(char *dest, uint64_t i, size_t digits);
```
Writes an unsigned binary number using `digits` bytes plus NUL.
#### `fio_ltoa_xbase`
```c
FIO_IFUNC void fio_ltoa_xbase(char *dest,
uint64_t i,
size_t digits,
size_t base);
```
Writes an unsigned number in `base` (up to 36) using `digits` bytes plus NUL.
### Helpers
#### `fio_c2i`
```c
IFUNC uint8_t fio_c2i(unsigned char c);
```
Maps a character to its numeric value (`0-9` → 0-9, `A-Z`/`a-z` → 10-35). Returns `255` for out-of-range characters.
#### `fio_i2c`
```c
IFUNC uint8_t fio_i2c(unsigned char i);
```
Maps a numeric value `0-35` to a character (`0-9`, `A-Z`). Out-of-range values above 35 produce undefined behavior; accepts values up to 63 by masking.
#### `fio_digits10`
```c
FIO_IFUNC size_t fio_digits10(int64_t i);
```
Returns the number of base-10 digits needed for `i`, including the sign.
#### `fio_digits10u`
```c
FIO_SFUNC size_t fio_digits10u(uint64_t i);
```
Returns the number of base-10 digits needed for an unsigned number.
#### `fio_digits8u`
```c
FIO_SFUNC size_t fio_digits8u(uint64_t i);
```
Returns the number of base-8 digits needed for an unsigned number.
#### `fio_digits16u`
```c
FIO_SFUNC size_t fio_digits16u(uint64_t i);
```
Returns the number of base-16 digits needed for an unsigned number, always an even count (2, 4, 6, ... 16).
#### `fio_digits_bin`
```c
FIO_SFUNC size_t fio_digits_bin(uint64_t i);
```
Returns the number of base-2 digits needed for an unsigned number, rounded up to an even count.
#### `fio_digits_xbase`
```c
FIO_SFUNC size_t fio_digits_xbase(uint64_t i, size_t base);
```
Returns the number of digits needed for an unsigned number in `base` (must be < 65).
#### `fio_u2i_limit`
```c
FIO_IFUNC int64_t fio_u2i_limit(uint64_t val, size_t invert);
```
Converts unsigned `val` to signed with overflow protection. If `invert` is zero, clamps to `INT64_MAX` and sets `errno = E2BIG` on overflow. If `invert` is non-zero, produces the negative value and clamps to `INT64_MIN`.
### IEEE 754 Helpers
#### `fio_i2d`
```c
FIO_IFUNC double fio_i2d(int64_t mant, int64_t exponent_in_base_2);
```
Converts a signed 64-bit mantissa and base-2 exponent to a `double`.
#### `fio_u2d`
```c
FIO_IFUNC double fio_u2d(uint64_t mant, int64_t exponent_in_base_2);
```
Converts an unsigned 64-bit mantissa and base-2 exponent to a `double`.
### Big Number Hex Conversion
#### `fio_u128_hex_read` / `fio_u256_hex_read` / `fio_u512_hex_read`
```c
SFUNC fio_u128 fio_u128_hex_read(char **pstr);
SFUNC fio_u256 fio_u256_hex_read(char **pstr);
SFUNC fio_u512 fio_u512_hex_read(char **pstr);
```
Reads a hex string and initializes the corresponding wide integer. Updates `*pstr` past the consumed input.
#### `fio_u1024_hex_read` / `fio_u2048_hex_read` / `fio_u4096_hex_read`
```c
SFUNC fio_u1024 fio_u1024_hex_read(char **pstr);
SFUNC fio_u2048 fio_u2048_hex_read(char **pstr);
SFUNC fio_u4096 fio_u4096_hex_read(char **pstr);
```
Same as above for 1024-, 2048-, and 4096-bit integers.
#### `fio_u128_hex_write` / `fio_u256_hex_write` / `fio_u512_hex_write`
```c
SFUNC size_t fio_u128_hex_write(char *dest, const fio_u128 *u);
SFUNC size_t fio_u256_hex_write(char *dest, const fio_u256 *u);
SFUNC size_t fio_u512_hex_write(char *dest, const fio_u512 *u);
```
Writes a wide integer as a hex string to `dest`. Returns bytes written excluding NUL.
#### `fio_u1024_hex_write` / `fio_u2048_hex_write` / `fio_u4096_hex_write`
```c
SFUNC size_t fio_u1024_hex_write(char *dest, const fio_u1024 *u);
SFUNC size_t fio_u2048_hex_write(char *dest, const fio_u2048 *u);
SFUNC size_t fio_u4096_hex_write(char *dest, const fio_u4096 *u);
```
Same as above for 1024-, 2048-, and 4096-bit integers.
### Example
```c
#define FIO_ATOL
#include "fio-stl.h"
int main(void) {
char *p = "0x1F 0b1010 42";
char buf[80];
printf("hex: %lld\n", (long long)fio_atol(&p));
++p; /* skip space */
printf("bin: %lld\n", (long long)fio_atol(&p));
++p;
printf("dec: %lld\n", (long long)fio_atol(&p));
fio_ltoa(buf, -255, 16);
printf("back to hex: %s\n", buf);
return 0;
}
```
------------------------------------------------------------
# CRC32
```c
#define FIO_CRC32
#include "fio-stl.h"
```
Fast CRC32 using the ITU-T V.42 / ISO 3309 / gzip polynomial (`0xEDB88320`). Useful for gzip, PNG, and anything else that expects this particular CRC32 flavor.
The implementation picks the best available path at compile time:
- ARM aarch64 with the CRC32 extension uses scalar CRC32 instructions, with a PMULL bulk-fold path when the crypto / PMULL feature is available.
- x86 / x64 with SSE4.2 and PCLMULQDQ uses a PCLMULQDQ bulk path and the software tail path.
- Other builds use the always-present slicing-by-16 software fallback.
**Note:** this is **not** CRC32-C (Castagnoli, polynomial `0x82F63B78`). The ARM scalar path uses the gzip-polynomial CRC32 instructions (`__crc32b`, `__crc32w`, `__crc32d`), not the Castagnoli variants.
### API Functions
#### `fio_crc32`
```c
SFUNC uint32_t fio_crc32(const void *data, size_t len, uint32_t initial_crc);
```
Computes the CRC32 of `len` bytes at `data`. Pass `0` for `initial_crc` to start fresh, or pass a previous result to continue an incremental checksum across multiple buffers.
**Returns:** the CRC32 checksum.
### Example — single buffer
```c
#define FIO_CRC32
#include "fio-stl.h"
int main(void) {
const char *msg = "Hello, World!";
uint32_t crc = fio_crc32(msg, strlen(msg), 0);
printf("CRC32: 0x%08X\n", crc);
return 0;
}
```
### Example — incremental
```c
uint32_t crc = 0;
crc = fio_crc32("Hello, ", 7, crc);
crc = fio_crc32("World!", 6, crc);
/* crc matches a single call over "Hello, World!" */
```
------------------------------------------------------------
# Glob Matching
```c
#define FIO_GLOB_MATCH
#include "fio-stl.h"
```
Binary glob matching for wildcard patterns. Bytes in, bytes out — no UTF-8 awareness, no allocation, just `*`, `?`, `[...]`, and escapes.
### API Functions
#### `fio_glob_match`
```c
SFUNC uint8_t fio_glob_match(fio_str_info_s pattern, fio_str_info_s string);
```
Returns `1` if `string` matches `pattern`, otherwise `0`.
### Supported Patterns
#### `*`
Matches any byte sequence, including an empty one.
#### `?`
Matches any single byte.
#### `[...]`
Matches any single byte inside the brackets. Ranges work with `-`, e.g. `[a-z]`. The first character may be `]` without closing the class. Use `\` to escape `]`, `-`, and `\` itself.
#### `[!...]` or `[^...]`
Inverted character class: matches any single byte **not** in the brackets.
#### `\`
Escapes the next character so it is matched literally.
### Example
```c
#define FIO_GLOB_MATCH
#define FIO_STR
#include "fio-stl.h"
int main(void) {
printf("%d\n", fio_glob_match(FIO_STR_INFO1("*.txt"),
FIO_STR_INFO1("notes.txt"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("log_????"),
FIO_STR_INFO1("log_2024"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("[!0-9]*"),
FIO_STR_INFO1("abc123"))); /* 1 */
printf("%d\n", fio_glob_match(FIO_STR_INFO1("file\\*"),
FIO_STR_INFO1("file*"))); /* 1 */
return 0;
}
```
------------------------------------------------------------
# Index Mapped Array (iMap)
```c
#define FIO_IMAP_CORE
#include "fio-stl.h"
```
A macro-generated hash-map-backed array. Keeps insertion order in a dense array while the index map provides near-O(1) lookups. Mostly used internally by other STL modules; for public map needs see [`FIO_MAP_NAME`](https://facil.io/edge/hash-maps-and-sets-module-210/).
**Note:** iMap does not manage object lifetimes. Clean up stored resources before calling destroy.
### Configuration Macros
#### `FIO_TYPEDEF_IMAP_REALLOC`
```c
#ifndef FIO_TYPEDEF_IMAP_REALLOC
#define FIO_TYPEDEF_IMAP_REALLOC FIO_MEM_REALLOC
#endif
```
Allocator used for iMap growth. Defaults to `FIO_MEM_REALLOC`.
#### `FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE`
```c
#ifndef FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE
#define FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE FIO_MEM_REALLOC_IS_SAFE
#endif
```
Set if the realloc zeroes new memory; otherwise iMap zeroes it explicitly.
#### `FIO_TYPEDEF_IMAP_FREE`
```c
#ifndef FIO_TYPEDEF_IMAP_FREE
#define FIO_TYPEDEF_IMAP_FREE FIO_MEM_FREE
#endif
```
Deallocator used by `array_name_destroy`.
### Helper Macros
#### `FIO_IMAP_ALWAYS_VALID`
```c
#define FIO_IMAP_ALWAYS_VALID(o) (1)
```
Validity helper that treats every object as valid.
#### `FIO_IMAP_VALID_NON_ZERO`
```c
#define FIO_IMAP_VALID_NON_ZERO(o) (!!((o)[0]))
```
Validity helper that treats an object as valid if its first element is non-zero.
#### `FIO_IMAP_ALWAYS_CMP_TRUE` / `FIO_IMAP_ALWAYS_CMP_FALSE`
```c
#define FIO_IMAP_ALWAYS_CMP_TRUE(a, b) (1)
#define FIO_IMAP_ALWAYS_CMP_FALSE(a, b) (0)
```
Comparison helpers that always return true or false.
#### `FIO_IMAP_SIMPLE_CMP`
```c
#define FIO_IMAP_SIMPLE_CMP(a, b) ((a)[0] == (b)[0])
```
Compares two objects by their first element.
#### `FIO_IMAP_EACH`
```c
#define FIO_IMAP_EACH(array_name, map_ptr, i) \
for (size_t i = 0; i < (map_ptr)->w; ++i) \
if (!FIO_NAME(array_name, is_valid)((map_ptr)->ary + i)) \
continue; \
else
```
Iterates valid elements in insertion order. Use it as a loop header:
```c
FIO_IMAP_EACH(my_map, &map, i) {
printf("%llu\n", (unsigned long long)map.ary[i].key);
}
```
### Type Definition Macro
#### `FIO_TYPEDEF_IMAP_ARRAY`
```c
#define FIO_TYPEDEF_IMAP_ARRAY(array_name, \
array_type, \
imap_type, \
hash_fn, \
cmp_fn, \
is_valid_fn)
```
Generates the iMap container type and functions prefixed with `array_name`. The callbacks take **pointers** to elements:
- `hash_fn(pobj)` returns an `imap_type` hash.
- `cmp_fn(a_ptr, b_ptr)` returns non-zero on match.
- `is_valid_fn(pobj)` returns non-zero if the element is valid.
Reserved index-map values: `0` means empty, `~0` means freed.
### Generated Types
#### `array_name_s`
```c
typedef struct {
array_type *ary;
imap_type count;
imap_type w;
uint32_t capa_bits;
} array_name_s;
```
- `ary` — dense array of elements.
- `count` — number of valid (non-removed) elements.
- `w` — next write position / logical size.
- `capa_bits` — log2 of capacity; actual capacity is `1 << capa_bits`.
#### `array_name_seeker_s`
```c
typedef struct {
imap_type pos;
imap_type ipos;
imap_type set_val;
} array_name_seeker_s;
```
- `pos` — array index, or `w` if not found.
- `ipos` — index-map slot, or `~0` if no room.
- `set_val` — value to write into the index map on insert.
### Generated Functions
#### `array_name_is_valid`
```c
FIO_IFUNC int array_name_is_valid(array_type *pobj);
```
Wraps the `is_valid_fn` supplied to the macro.
#### `array_name_capa`
```c
FIO_IFUNC size_t array_name_capa(array_name_s *a);
```
Returns `1 << a->capa_bits`, or `0` if not allocated.
#### `array_name_imap`
```c
FIO_IFUNC imap_type *array_name_imap(array_name_s *a);
```
Returns a pointer to the index map stored immediately after the array.
#### `array_name_destroy`
```c
FIO_IFUNC void array_name_destroy(array_name_s *a);
```
Frees the array + index map and zeros the container. Does not run destructors on elements.
#### `array_name_seek`
```c
FIO_SFUNC array_name_seeker_s array_name_seek(array_name_s *a,
array_type *pobj);
```
Finds an element or the place to insert it.
#### `array_name_reserve`
```c
FIO_IFUNC int array_name_reserve(array_name_s *a, imap_type min);
```
Grows the backing storage to at least `min` slots. Returns `0` on success, `-1` on failure.
#### `array_name_rehash`
```c
FIO_IFUNC int array_name_rehash(array_name_s *a);
```
Rebuilds the index map from the current array contents. Call after sorting or other position-changing operations. Returns `0` on success, `-1` on failure.
#### `array_name_set`
```c
FIO_IFUNC array_type *array_name_set(array_name_s *a,
array_type obj,
int overwrite);
```
Inserts or updates an element. If `overwrite` is zero and the key already exists, returns the existing element. Returns a pointer to the stored element or `NULL` on failure.
#### `array_name_get`
```c
FIO_IFUNC array_type *array_name_get(array_name_s *a, array_type obj);
```
Looks up an element. Returns a pointer to the match or `NULL`.
#### `array_name_remove`
```c
FIO_IFUNC int array_name_remove(array_name_s *a, array_type obj);
```
Zeros the matching element and marks its index-map slot as freed. Returns `0` on success, `-1` if not found.
### Internal Seeker Types
#### `fio___imapN_seeker_s`
```c
typedef struct {
uintN_t pos;
uintN_t ipos;
uintN_t set_val;
bool is_valid;
} fio___imapN_seeker_s;
```
Where `N` is `8`, `16`, `32`, or `64`. Low-level seeker used by other STL internals.
#### `fio___imapN_seek`
```c
FIO_SFUNC fio___imapN_seeker_s fio___imapN_seek(
void *ary,
uintN_t *imap,
const uintN_t capa_bits,
void *pobj,
uintN_t hash,
bool cmp_fn(void *arry, void *obj, uintN_t indx),
const size_t max_attempts);
```
Low-level seek through an index map. Generally used internally.
#### `fio___imapN_set`
```c
FIO_IFUNC void fio___imapN_set(uintN_t *imap,
uintN_t ipos,
uintN_t set_val);
```
Writes `set_val` into `imap[ipos]`.
### Example
```c
#define FIO_IMAP_CORE
#include "fio-stl.h"
typedef struct { uint64_t key; int value; } kv_s;
static uint64_t kv_hash(kv_s *p) { return fio_risky_num(p->key, 0); }
static int kv_cmp(kv_s *a, kv_s *b) { return a->key == b->key; }
static int kv_valid(kv_s *p) { return p->key != 0; }
FIO_TYPEDEF_IMAP_ARRAY(kv, kv_s, uint32_t, kv_hash, kv_cmp, kv_valid)
int main(void) {
kv_s map = {0};
kv_set(&map, (kv_s){.key = 1, .value = 100}, 1);
kv_set(&map, (kv_s){.key = 2, .value = 200}, 1);
kv_s *found = kv_get(&map, (kv_s){.key = 2});
if (found) printf("value: %d\n", found->value);
FIO_IMAP_EACH(kv, &map, i) {
printf("%llu -> %d\n",
(unsigned long long)map.ary[i].key, map.ary[i].value);
}
kv_remove(&map, (kv_s){.key = 2});
kv_destroy(&map);
return 0;
}
```
------------------------------------------------------------
# Multi-Precision Math
```c
#define FIO_MATH
#include "fio-stl.h"
```
Extra multi-precision operations on little-endian `uint64_t` arrays: division, shifts, two's-complement inverse, and bit-index queries. The core add/sub/mul helpers (`fio_math_add`, `fio_math_sub`, `fio_math_mul`, `fio_math_addc64`, etc.) and the wide union types (`fio_u128`, `fio_u256`, …) live in [`000 core.h`](https://facil.io/edge/facil-io-stl-core/) and do not require `FIO_MATH`.
**Note:** this module assumes the CPU performs 64-bit multiply in constant time, which is not guaranteed on all hardware.
### API Functions
#### `fio_math_div`
```c
FIO_IFUNC void fio_math_div(uint64_t *dest,
uint64_t *reminder,
const uint64_t *a,
const uint64_t *b,
const size_t number_array_length);
```
Divides the `number_array_length`-word number `a` by `b`, storing the quotient in `dest` and the remainder in `reminder`. Either output may be `NULL` if you only need the other one.
**Note:** not constant time. Uses binary long division, which is simple but not the fastest algorithm on earth.
If `b` is zero, logs an error and fills both outputs with `0xFF`.
#### `fio_math_shr`
```c
FIO_IFUNC void fio_math_shr(uint64_t *dest,
uint64_t *n,
const size_t right_shift_bits,
size_t number_array_length);
```
Shifts `n` right by `right_shift_bits` and stores the result in `dest`.
#### `fio_math_shl`
```c
FIO_IFUNC void fio_math_shl(uint64_t *dest,
uint64_t *n,
const size_t left_shift_bits,
const size_t number_array_length);
```
Shifts `n` left by `left_shift_bits` and stores the result in `dest`.
#### `fio_math_inv`
```c
FIO_IFUNC void fio_math_inv(uint64_t *dest, uint64_t *n, size_t len);
```
Two's-complement negation of the `len`-word number `n`.
#### `fio_math_msb_index`
```c
FIO_MIFN size_t fio_math_msb_index(uint64_t *n, const size_t len);
```
Returns the zero-based index of the most significant set bit, or `(size_t)-1` if the number is zero. Useful for measuring bit length.
#### `fio_math_lsb_index`
```c
FIO_MIFN size_t fio_math_lsb_index(uint64_t *n, const size_t len);
```
Returns the zero-based index of the least significant set bit, or `(size_t)-1` if the number is zero.
### Example
```c
#define FIO_MATH
#include "fio-stl.h"
int main(void) {
uint64_t a[2] = {0, 1}; /* 2^64 */
uint64_t b[2] = {3, 0}; /* 3 */
uint64_t q[2] = {0};
uint64_t r[2] = {0};
fio_math_div(q, r, a, b, 2);
printf("quot: %016llX%016llX\n",
(unsigned long long)q[1], (unsigned long long)q[0]);
printf("rem: %016llX%016llX\n",
(unsigned long long)r[1], (unsigned long long)r[0]);
return 0;
}
```
------------------------------------------------------------
# Pseudo-Random and Hashing
```c
#define FIO_RAND
#include "fio-stl.h"
```
A non-cryptographic pseudo-random generator plus RiskyHash and Stable Hash. The PRNG is seeded from system jitter (`getrusage`, clocks) and hashed with RiskyHash. It is faster and more random than typical `rand()` implementations, but **do not use it for cryptography** — use `fio_rand_bytes_secure` for that.
RiskyHash is an ephemeral, non-cryptographic hash family that may change between releases. Stable Hash is frozen and safe for persistent data. RiskyHash 256/512 and HMAC variants are also provided.
### Pseudo-Random Functions
#### `fio_rand64`
```c
SFUNC uint64_t fio_rand64(void);
```
Returns 64 pseudo-random bits.
#### `fio_rand128`
```c
SFUNC fio_u128 fio_rand128(void);
```
Returns 128 pseudo-random bits as a `fio_u128`.
#### `fio_rand_bytes`
```c
SFUNC void fio_rand_bytes(void *target, size_t len);
```
Fills `target` with `len` pseudo-random bytes.
#### `fio_rand_bytes_secure`
```c
SFUNC int fio_rand_bytes_secure(void *target, size_t len);
```
Fills `target` with `len` cryptographically secure random bytes from the system CSPRNG: `arc4random_buf` on BSD/macOS, `BCryptGenRandom` on Windows, `getrandom` on Linux, and `/dev/urandom` as the fallback (also used when `getrandom` is unavailable or blocked). Returns `0` on success, `-1` on failure (no partial success; never falls back to the non-cryptographic PRNG). A `len` of `0` succeeds; a `NULL` `target` with `len > 0` fails.
**Windows linking**: MSVC links `bcrypt.lib` automatically (`#pragma comment`); MinGW / GNU-driver builds must link `-lbcrypt`.
Use this for keys, nonces, and anything security-sensitive.
#### `fio_rand_reseed`
```c
SFUNC void fio_rand_reseed(void);
```
Rotates the PRNG state with fresh entropy. Call after `fork` to avoid duplicated sequences.
### Risky / Stable Hash
#### `FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH`
```c
#define FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH 0
```
When set to `1`, `fio_risky_hash` redirects to `fio_stable_hash`.
#### `fio_risky_hash`
```c
SFUNC uint64_t fio_risky_hash(const void *buf, size_t len, uint64_t seed);
```
64-bit RiskyHash v.3. Ephemeral — hash values may change in future releases. Good for hash-map keys, not for storage.
#### `fio_risky_ptr`
```c
FIO_IFUNC uint64_t fio_risky_ptr(void *ptr);
```
Fast entropy mix for pointer values.
#### `fio_risky_num`
```c
FIO_IFUNC uint64_t fio_risky_num(uint64_t number, uint64_t seed);
```
Fast entropy mix for integer values.
#### `fio_stable_hash`
```c
SFUNC uint64_t fio_stable_hash(const void *data, size_t len, uint64_t seed);
```
64-bit Stable Hash. Frozen after 1.0; safe for on-disk or cross-system use.
#### `fio_stable_hash128`
```c
SFUNC void fio_stable_hash128(void *restrict dest,
const void *restrict data,
size_t len,
uint64_t seed);
```
Writes a 128-bit Stable Hash into `dest` (16 bytes).
#### `fio_risky256`
```c
SFUNC fio_u256 fio_risky256(const void *data, uint64_t len);
```
256-bit RiskyHash. Based on the A3 (Zero-Copy ILP) design: two 512-bit states, 128 bytes per iteration, multiply-fold with cross-lane mixing. Returns a `fio_u256`.
#### `fio_risky512`
```c
SFUNC fio_u512 fio_risky512(const void *data, uint64_t len);
```
512-bit RiskyHash. The first 256 bits are identical to `fio_risky256`; the rest come from an extra squeeze round.
#### `fio_risky256_hmac`
```c
SFUNC fio_u256 fio_risky256_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
RFC 2104 HMAC using `fio_risky256` as the hash, 64-byte block size. Keys longer than 64 bytes are hashed first. Securely zeros intermediates.
**Note:** the underlying hash is non-cryptographic; standard HMAC security proofs do not apply. Use Blake2 for cryptographic HMAC.
#### `fio_risky512_hmac`
```c
SFUNC fio_u512 fio_risky512_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Same construction as `fio_risky256_hmac`, but with `fio_risky512` and a 64-byte digest.
### Example
```c
#define FIO_RAND
#include "fio-stl.h"
int main(void) {
printf("rand64: %016llX\n", (unsigned long long)fio_rand64());
uint8_t key[32];
fio_rand_bytes_secure(key, sizeof(key));
uint64_t h = fio_stable_hash("hello", 5, 0);
printf("stable: %016llX\n", (unsigned long long)h);
fio_u256 r = fio_risky256("hello", 5);
printf("risky256: %016llX...\n",
(unsigned long long)r.u64[0]);
return 0;
}
```
------------------------------------------------------------
# Signal Monitoring
```c
#define FIO_SIGNAL
#include "fio-stl.h"
```
A small wrapper around OS signals. It sets a handler, records that the signal fired, and lets you call the real callback later from normal execution context — the safe pattern, since signal handlers are heavily restricted. POSIX and Windows are supported.
### Configuration Macros
#### `FIO_SIGNAL_MONITOR_MAX`
```c
#ifndef FIO_SIGNAL_MONITOR_MAX
#define FIO_SIGNAL_MONITOR_MAX 24
#endif
```
Maximum number of signals that can be monitored at once.
### Portable Signal Aliases
#### `FIO_SIGNAL_USER1` / `FIO_SIGNAL_USER2` / `FIO_SIGNAL_USER_UNREGISTERED`
```c
#if FIO_OS_POSIX
#define FIO_SIGNAL_USER1 SIGUSR1
#define FIO_SIGNAL_USER2 SIGUSR2
#define FIO_SIGNAL_USER_UNREGISTERED SIGUSR2
#elif FIO_OS_WIN
#define FIO_SIGNAL_USER1 SIGBREAK
#define FIO_SIGNAL_USER2 SIGABRT
#define FIO_SIGNAL_USER_UNREGISTERED SIGBREAK
#endif
```
Maps user signals across POSIX and Windows.
### Types
#### `fio_signal_monitor_args_s`
```c
typedef struct {
int sig;
void (*callback)(int sig, void *udata);
void *udata;
bool propagate;
bool immediate;
} fio_signal_monitor_args_s;
```
- `sig` — signal number to watch.
- `callback` — function to run; `NULL` ignores the signal.
- `udata` — opaque pointer passed to the callback.
- `propagate` — if true, call any previous handler after recording the signal.
- `immediate` — if true, run the callback inside the signal handler; otherwise defer to `fio_signal_review`.
### API Functions
#### `fio_signal_monitor`
```c
SFUNC int fio_signal_monitor(fio_signal_monitor_args_s args);
#define fio_signal_monitor(...) \
fio_signal_monitor((fio_signal_monitor_args_s){__VA_ARGS__})
```
Starts monitoring `sig`. Updating an existing monitor replaces its callback and `udata`. Returns `0` on success, `-1` on error.
**Note:** `immediate` callbacks must be async-signal-safe. Most code is not.
#### `fio_signal_review`
```c
SFUNC int fio_signal_review(void);
```
Calls deferred callbacks for any signals that fired since the last review. Returns the number of callbacks invoked.
#### `fio_signal_forget`
```c
SFUNC int fio_signal_forget(int sig);
```
Stops monitoring `sig` and restores the default handler. Returns `0` on success, `-1` on error.
### Example
```c
#define FIO_SIGNAL
#define FIO_LOG
#include "fio-stl.h"
static volatile int running = 1;
static void on_sigint(int sig, void *udata) {
(void)sig; (void)udata;
FIO_LOG_INFO("SIGINT received, shutting down...");
running = 0;
}
int main(void) {
fio_signal_monitor(.sig = SIGINT,
.callback = on_sigint,
.udata = NULL);
while (running) {
fio_signal_review();
/* do work */
}
fio_signal_forget(SIGINT);
return 0;
}
```
------------------------------------------------------------
# Sorting
```c
#define FIO_SORT_NAME num
#define FIO_SORT_TYPE size_t
#include "fio-stl.h"
```
A macro-generated quicksort + insert-sort combo. Define `FIO_SORT_NAME` and `FIO_SORT_TYPE` before including the header to get a set of sort functions named after your type. Include the header multiple times to sort different types.
### Configuration Macros
#### `FIO_SORT_NAME`
```c
#define FIO_SORT_NAME num
```
Prefix for generated function names: `num_sort`, `num_qsort`, `num_isort`.
#### `FIO_SORT_TYPE`
```c
#define FIO_SORT_TYPE size_t
```
The element type. **Required.**
#### `FIO_SORT_IS_BIGGER`
```c
#ifndef FIO_SORT_IS_BIGGER
#define FIO_SORT_IS_BIGGER(a, b) ((a) > (b))
#endif
```
Must evaluate to `1` if `a > b`, `0` otherwise. Override for custom ordering.
#### `FIO_SORT_SWAP`
```c
#ifndef FIO_SORT_SWAP
#define FIO_SORT_SWAP(a, b) \
do { \
FIO_SORT_TYPE tmp__ = (a); \
(a) = (b); \
(b) = tmp__; \
} while (0)
#endif
```
Swaps two elements. Override if your type needs a custom swap.
#### `FIO_SORT_THRESHOLD`
```c
#ifndef FIO_SORT_THRESHOLD
#define FIO_SORT_THRESHOLD 96
#endif
```
Partition size below which quicksort switches to insert-sort.
### API Functions
#### `FIO_SORT_NAME_sort`
```c
FIO_IFUNC void FIO_NAME(FIO_SORT_NAME, sort)(FIO_SORT_TYPE *array,
size_t count);
```
Sorts `array[0..count-1]` in ascending order. Currently dispatches to quicksort.
#### `FIO_SORT_NAME_qsort`
```c
SFUNC void FIO_NAME(FIO_SORT_NAME, qsort)(FIO_SORT_TYPE *array, size_t count);
```
Non-recursive quicksort with median-of-three pivot selection. Falls back to insert-sort for small partitions.
#### `FIO_SORT_NAME_isort`
```c
SFUNC void FIO_NAME(FIO_SORT_NAME, isort)(FIO_SORT_TYPE *array, size_t count);
```
Insert-sort for small or nearly-sorted arrays. O(n²), so avoid large inputs.
### Example
```c
#define FIO_SORT_NAME int
#define FIO_SORT_TYPE int
#include "fio-stl.h"
int main(void) {
int numbers[] = {5, 2, 8, 1, 9, 3, 7, 4, 6, 0};
size_t count = sizeof(numbers) / sizeof(numbers[0]);
int_sort(numbers, count);
for (size_t i = 0; i < count; ++i)
printf("%d ", numbers[i]);
printf("\n");
return 0;
}
```
------------------------------------------------------------
# Portable Threads
```c
#define FIO_THREADS
#include "fio-stl.h"
```
A thin, portable wrapper over POSIX pthreads and Windows threads. Provides thread creation, mutexes, condition variables, priorities, and a few process helpers. The wrappers intentionally return no thread exit value.
### Types
#### `fio_thread_t`
```c
/* POSIX */ typedef pthread_t fio_thread_t;
/* Windows */ typedef uintptr_t fio_thread_t;
```
Thread handle. On Windows this is the numeric thread ID; the handle is opened transiently only when needed.
#### `fio_thread_pid_t`
```c
/* POSIX */ typedef pid_t fio_thread_pid_t;
/* Windows */ typedef DWORD fio_thread_pid_t;
```
Process ID type.
#### `fio_thread_mutex_t`
```c
/* POSIX */ typedef pthread_mutex_t fio_thread_mutex_t;
/* Windows */ typedef SRWLOCK fio_thread_mutex_t;
```
Mutex type.
#### `fio_thread_cond_t`
```c
/* POSIX */ typedef pthread_cond_t fio_thread_cond_t;
/* Windows */ typedef CONDITION_VARIABLE fio_thread_cond_t;
```
Condition-variable type.
#### `fio_thread_priority_e`
```c
typedef enum {
FIO_THREAD_PRIORITY_ERROR = -1,
FIO_THREAD_PRIORITY_LOWEST = 0,
FIO_THREAD_PRIORITY_LOW,
FIO_THREAD_PRIORITY_NORMAL,
FIO_THREAD_PRIORITY_HIGH,
FIO_THREAD_PRIORITY_HIGHEST,
} fio_thread_priority_e;
```
Thread priority levels.
### Configuration Macros
#### `FIO_THREADS_BYO`
If defined, the thread creation/join/detach/current/yield functions are declared but not implemented — bring your own.
#### `FIO_THREADS_FORK_BYO`
If defined, the process helpers (`fio_thread_fork`, `getpid`, `kill`, `waitpid`) are declared but not implemented.
#### `FIO_THREADS_MUTEX_BYO`
If defined, the mutex functions are declared but not implemented.
#### `FIO_THREADS_COND_BYO`
If defined, the condition-variable functions are declared but not implemented.
#### `FIO_THREAD_MUTEX_INIT`
```c
/* POSIX */ #define FIO_THREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
/* Windows */ #define FIO_THREAD_MUTEX_INIT { 0 }
```
Static initializer for a mutex.
### Process Functions
#### `fio_thread_fork`
```c
FIO_IFUNC fio_thread_pid_t fio_thread_fork(void);
```
Behaves like POSIX `fork`. On Windows logs an error and returns `-1`.
#### `fio_thread_getpid`
```c
FIO_IFUNC fio_thread_pid_t fio_thread_getpid(void);
```
Behaves like POSIX `getpid`.
#### `fio_thread_kill`
```c
FIO_IFUNC int fio_thread_kill(fio_thread_pid_t pid, int sig);
```
Behaves like POSIX `kill`.
#### `fio_thread_waitpid`
```c
FIO_IFUNC int fio_thread_waitpid(fio_thread_pid_t pid,
int *stat_loc,
int options);
```
Behaves like POSIX `waitpid`.
### Thread Functions
#### `fio_thread_create`
```c
FIO_IFUNC int fio_thread_create(fio_thread_t *t,
void *(*fn)(void *),
void *arg);
```
Starts a new thread running `fn(arg)`. Returns `0` on success, `-1` on failure.
#### `fio_thread_join`
```c
FIO_IFUNC int fio_thread_join(fio_thread_t *t);
```
Waits for the thread to finish.
#### `fio_thread_detach`
```c
FIO_IFUNC int fio_thread_detach(fio_thread_t *t);
```
Detaches the thread so resources are reclaimed automatically.
#### `fio_thread_exit`
```c
FIO_IFUNC void fio_thread_exit(void);
```
Exits the current thread.
#### `fio_thread_equal`
```c
FIO_IFUNC int fio_thread_equal(fio_thread_t *a, fio_thread_t *b);
```
Returns non-zero if `a` and `b` refer to the same thread.
#### `fio_thread_current`
```c
FIO_IFUNC fio_thread_t fio_thread_current(void);
```
Returns a handle for the current thread.
#### `fio_thread_nid`
```c
FIO_IFUNC uintptr_t fio_thread_nid(void);
```
Returns a process-local numeral ID for the current thread. The value is stable
for the thread's lifetime and unique among live threads, but may be reused after
the thread exits. It is not guaranteed to be an OS-visible kernel thread ID.
#### `fio_thread_yield`
```c
FIO_IFUNC void fio_thread_yield(void);
```
Yields the CPU.
#### `fio_thread_priority`
```c
FIO_SFUNC fio_thread_priority_e fio_thread_priority(void);
```
Returns the current thread's priority, or `FIO_THREAD_PRIORITY_ERROR` on failure.
#### `fio_thread_priority_set`
```c
FIO_SFUNC int fio_thread_priority_set(fio_thread_priority_e pr);
```
Sets the current thread's priority. Returns `0` on success, `-1` on error.
### Mutex Functions
#### `fio_thread_mutex_init`
```c
FIO_IFUNC int fio_thread_mutex_init(fio_thread_mutex_t *m);
```
Initializes a mutex. You can also use `FIO_THREAD_MUTEX_INIT` for static initialization.
#### `fio_thread_mutex_lock`
```c
FIO_IFUNC int fio_thread_mutex_lock(fio_thread_mutex_t *m);
```
Locks `m`, blocking if necessary.
#### `fio_thread_mutex_trylock`
```c
FIO_IFUNC int fio_thread_mutex_trylock(fio_thread_mutex_t *m);
```
Tries to lock `m` without blocking. Returns `0` on success.
#### `fio_thread_mutex_unlock`
```c
FIO_IFUNC int fio_thread_mutex_unlock(fio_thread_mutex_t *m);
```
Unlocks `m`.
#### `fio_thread_mutex_destroy`
```c
FIO_IFUNC void fio_thread_mutex_destroy(fio_thread_mutex_t *m);
```
Destroys `m`.
### Condition Variable Functions
#### `fio_thread_cond_init`
```c
FIO_IFUNC int fio_thread_cond_init(fio_thread_cond_t *c);
```
Initializes a condition variable.
#### `fio_thread_cond_wait`
```c
FIO_IFUNC int fio_thread_cond_wait(fio_thread_cond_t *c,
fio_thread_mutex_t *m);
```
Waits on `c`. `m` must be locked; it is released while waiting and re-acquired on return.
#### `fio_thread_cond_timedwait`
```c
FIO_IFUNC int fio_thread_cond_timedwait(fio_thread_cond_t *c,
fio_thread_mutex_t *m,
size_t milliseconds);
```
Waits up to `milliseconds`. Returns `0` if signaled, non-zero on timeout/error.
#### `fio_thread_cond_signal`
```c
FIO_IFUNC int fio_thread_cond_signal(fio_thread_cond_t *c);
```
Wakes one waiter.
#### `fio_thread_cond_broadcast`
```c
FIO_IFUNC int fio_thread_cond_broadcast(fio_thread_cond_t *c);
```
Wakes all waiters.
#### `fio_thread_cond_destroy`
```c
FIO_IFUNC void fio_thread_cond_destroy(fio_thread_cond_t *c);
```
Destroys `c`.
### Example
```c
#define FIO_THREADS
#include "fio-stl.h"
static fio_thread_mutex_t lock = FIO_THREAD_MUTEX_INIT;
static int counter = 0;
static void *worker(void *arg) {
(void)arg;
for (int i = 0; i < 100000; ++i) {
fio_thread_mutex_lock(&lock);
++counter;
fio_thread_mutex_unlock(&lock);
}
return NULL;
}
int main(void) {
fio_thread_t t1, t2;
fio_thread_create(&t1, worker, NULL);
fio_thread_create(&t2, worker, NULL);
fio_thread_join(&t1);
fio_thread_join(&t2);
printf("counter: %d\n", counter);
fio_thread_mutex_destroy(&lock);
return 0;
}
```
------------------------------------------------------------
# URL / URI Parsing
```c
#define FIO_URL
#include "fio-stl.h"
```
Zero-copy, zero-allocation URI parser. Returns `fio_buf_info_s` slices pointing into the original string. No decoding is performed; pass the raw, encoded URI.
### Types
#### `fio_url_s`
```c
typedef struct {
fio_buf_info_s scheme;
fio_buf_info_s user;
fio_buf_info_s password;
fio_buf_info_s host;
fio_buf_info_s port;
fio_buf_info_s path;
fio_buf_info_s query;
fio_buf_info_s target;
} fio_url_s;
```
Parsed URL components. Empty components have `.buf == NULL` and `.len == 0`. Strings are **not** NUL terminated.
#### `fio_url_tls_info_s`
```c
typedef struct {
fio_buf_info_s key;
fio_buf_info_s cert;
fio_buf_info_s pass;
bool tls;
} fio_url_tls_info_s;
```
TLS material extracted from scheme or query parameters.
#### `fio_url_query_each_s`
```c
typedef struct {
fio_buf_info_s name;
fio_buf_info_s value;
fio_buf_info_s private___;
} fio_url_query_each_s;
```
Iterator state for `FIO_URL_QUERY_EACH`. Do not touch `private___`.
### API Functions
#### `fio_url_parse`
```c
SFUNC fio_url_s fio_url_parse(const char *url, size_t len);
```
Parses `url` of length `len` into components. Accepts many shapes:
- `/path?query#target`
- `host:port/path?query#target`
- `user:password@host:port/path?query#target`
- `scheme://user:password@host:port/path?query#target`
Invalid input produces unpredictable results — the parser does not validate.
**Note:** `file`, `unix`, and `priv` schemes are treated as file paths; the parser puts the whole remainder into `path` and clears host/port/user/password.
#### `fio_url_is_tls`
```c
SFUNC fio_url_tls_info_s fio_url_is_tls(fio_url_s u);
```
Detects TLS from scheme or query. Implicit TLS schemes include `wss`, `https`, `sses`, `ssse`, `tcps`, `stcp`, `tls`, `ssl`, `udps`, `sudp`. Explicit TLS is enabled by query keys `tls`, `key`, `cert`, `pass`, or `password`.
### Macros
#### `FIO_URL_QUERY_EACH`
```c
#define FIO_URL_QUERY_EACH(query_buf, i) \
for (fio_url_query_each_s i = fio_url_query_each_next( \
(fio_url_query_each_s){.private___ = (query_buf)}); \
i.name.buf; \
i = fio_url_query_each_next(i))
```
Iterates over query parameters. The iterator does not URL-decode names or values.
### Helper Functions
#### `fio_url_query_each_next`
```c
FIO_SFUNC fio_url_query_each_s fio_url_query_each_next(fio_url_query_each_s i);
```
Advances the query iterator. Usually called only through `FIO_URL_QUERY_EACH`.
### Example
```c
#define FIO_URL
#include "fio-stl.h"
int main(void) {
const char *s = "https://user:pass@example.com:8443/path?k=v#section";
fio_url_s u = fio_url_parse(s, strlen(s));
printf("scheme: %.*s\n", (int)u.scheme.len, u.scheme.buf);
printf("host: %.*s\n", (int)u.host.len, u.host.buf);
printf("port: %.*s\n", (int)u.port.len, u.port.buf);
printf("path: %.*s\n", (int)u.path.len, u.path.buf);
FIO_URL_QUERY_EACH(u.query, p) {
printf("%.*s = %.*s\n",
(int)p.name.len, p.name.buf,
(int)p.value.len, p.value.buf);
}
fio_url_tls_info_s tls = fio_url_is_tls(u);
printf("tls: %d\n", tls.tls);
return 0;
}
```
------------------------------------------------------------
# ML Entity Decoding
```c
#define FIO_ENTITY
#include "fio-stl.h"
```
Decodes a single Markup Language entity (`&name;`, `digits;`, `hex;`) into UTF-8. Handy when you need to unescape one entity at a time, for example while scanning HTML or XML text.
### API Functions
#### `fio_entity`
```c
SFUNC size_t fio_entity(char *dest, const char *src, size_t len);
```
Decodes the entity starting at `src` (length `len`). Writes the UTF-8 result to `dest`, which must be at least 8 bytes. A NUL terminator is written only if the result is shorter than 8 bytes.
**Returns:** the number of bytes written (excluding NUL), or `0` if `src` does not start with a valid entity.
Supported forms:
- Named: `&name;` — case-insensitive, ~40 common entities such as `<`, `&`, `©`, `—`.
- Decimal: `digits;`
- Hex: `hex;` or `hex;`
`` is replaced with the Unicode replacement character (`U+FFFD`). Code points above `0x10FFFF` are rejected.
### Example
```c
#define FIO_ENTITY
#include "fio-stl.h"
int main(void) {
const char *s = "© 2026 😀";
char buf[8];
size_t n = fio_entity(buf, s, strlen(s));
printf("decoded: %.*s\n", (int)n, buf);
return 0;
}
```
------------------------------------------------------------
# File Helpers
```c
#define FIO_FILES
#include "fio-stl.h"
```
POSIX-style file helpers: open, read, write, remove, folder creation, size, type, path safety, filename parsing, and temporary files. Nothing fancy — just the usual paperwork. Implemented in [`./004 files.h`](https://facil.io/edge/api/004-files/).
### Configuration Macros
#### `FIO_FD_FIND_BLOCK`
```c
#ifndef FIO_FD_FIND_BLOCK
#define FIO_FD_FIND_BLOCK 4096
#endif
```
Stack buffer size used by `fio_fd_find_next` for each read cycle. Override before inclusion if you want larger or smaller scans.
#### `FIO_FILENAME_PATH_CAPA`
```c
#ifndef FIO_FILENAME_PATH_CAPA
#define FIO_FILENAME_PATH_CAPA (PATH_MAX | 4094)
#endif
```
Stack buffer capacity used for path manipulation (`"~/"` expansion in `fio_filename_open`, recursive `fio_filename_remove`, nested `fio_filename_make_path`). These functions allocate nothing — paths longer than this fail with `errno == ENAMETOOLONG`. The default guarantees at least ~4KB.
### Types
#### `fio_filename_s`
```c
typedef struct {
fio_buf_info_s folder;
fio_buf_info_s basename;
fio_buf_info_s ext;
} fio_filename_s;
```
Zero-copy result from `fio_filename_parse` / `fio_filename_parse2`. All members point into the original path string.
**Members:**
- `folder` - directory path including trailing separator, or `{NULL,0}` if none
- `basename` - file name without extension, or `{NULL,0}` if empty
- `ext` - extension without leading `.`, or `{NULL,0}` if none
### Macros
#### `FIO_FOLDER_SEPARATOR`
```c
#if FIO_OS_WIN
#define FIO_FOLDER_SEPARATOR '\\'
#else
#define FIO_FOLDER_SEPARATOR '/'
#endif
```
Native path separator. On Windows the parser also accepts `/`.
#### `fio_file_dup`
```c
#if FIO_OS_WIN
#define fio_file_dup(fd) _dup(fd)
#else
#define fio_file_dup(fd) dup(fd)
#endif
```
Duplicates a file descriptor.
#### `fio_filename_is_folder`
```c
#define fio_filename_is_folder(filename) \
(fio_filename_type((filename)) == S_IFDIR)
```
Returns non-zero if `filename` is a directory, `0` otherwise (including errors).
#### `FIO_FD_FIND_EOF`
```c
#define FIO_FD_FIND_EOF ((size_t)-1)
```
Sentinel returned by `fio_fd_find_next` when the token is not found before EOF.
### Opening and Creating Files
#### `fio_filename_open`
```c
SFUNC int fio_filename_open(const char *filename, int flags);
```
Opens `filename` with POSIX `open` semantics. If `filename` starts with `"~/"` (or `"~\\"` on Windows), it is resolved relative to `$HOME`.
**Parameters:**
- `filename` - path to open
- `flags` - `O_RDONLY`, `O_RDWR | O_CREAT | O_TRUNC`, etc.
**Returns:** file descriptor on success, `-1` on error.
**Note:** uses a fixed size stack buffer (zero allocations); over-long `"~/..."` paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
#### `fio_filename_tmp`
```c
SFUNC int fio_filename_tmp(void);
```
Creates a temporary file and returns its descriptor. Tries `TMPDIR`, `TMP`, `TEMP`, `P_tmpdir`, then the current directory. On Linux it attempts `O_TMPFILE` first; otherwise it builds a name prefixed with `facil_io_tmp_` and opens with `O_CREAT | O_EXCL`.
**Returns:** file descriptor on success, `-1` on error.
#### `fio_filename_overwrite`
```c
FIO_IFUNC int fio_filename_overwrite(const char *filename,
const void *buf,
size_t len);
```
Opens `filename` with `O_RDWR | O_CREAT | O_TRUNC`, writes `len` bytes from `buf`, and closes the file.
**Returns:** `0` on success, `-1` on error. On error the file state is undefined.
### Removing Files and Folders
#### `fio_filename_remove`
```c
typedef struct {
const char *path;
uint8_t folder;
uint8_t recursive;
} fio_filename_remove_args_s;
SFUNC int fio_filename_remove(fio_filename_remove_args_s args);
#define fio_filename_remove(...) \
fio_filename_remove((fio_filename_remove_args_s){__VA_ARGS__})
```
Removes the file / link or folder at `path`, abstracting OS differences (`unlink` / `rmdir` vs. `DeleteFileA` / `RemoveDirectoryA`).
**Parameters:**
- `path` - NUL-terminated path of the file / folder to remove
- `folder` - set to allow removal of an empty folder (like `rmdir`)
- `recursive` - set to remove a folder and all of its content (like `rm -r`); implies `folder`. If `path` isn't a folder, the flag is ignored and `path` is removed as a file / link
**Returns:** `0` on success, `-1` on error. Recursive removal stops on the first error (some content may remain). Uses a fixed size stack buffer (zero allocations); over-long paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
**Note:** links (symlinks / reparse points) are removed, never followed into.
**Example:**
```c
fio_filename_remove(.path = "old.log");
fio_filename_remove(.path = "empty_dir", .folder = 1);
fio_filename_remove(.path = "build_tree", .recursive = 1);
```
### Creating Folders
#### `fio_filename_make_path`
```c
typedef struct {
const char *path;
uint32_t mode;
} fio_filename_make_path_args_s;
SFUNC int fio_filename_make_path(fio_filename_make_path_args_s args);
#define fio_filename_make_path(...) \
fio_filename_make_path((fio_filename_make_path_args_s){__VA_ARGS__})
```
Creates the folder at `path`, including any missing parent folders (similar to `mkdir -p`). Abstracts OS differences (`mkdir` vs. `CreateDirectoryA`).
**Parameters:**
- `path` - NUL-terminated folder path to create; nested folders are created as needed, trailing separators are allowed
- `mode` - creation mode (POSIX only, ignored on Windows); zero defaults to `0755`
**Returns:** `0` on success, `-1` on error. An existing folder is NOT an error. A non-folder component along the way IS an error (`errno == ENOTDIR`). Uses a fixed size stack buffer (zero allocations); over-long paths fail with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
**Note:** root / drive / UNC `\\server\share` prefixes are never created, only the components after them.
**Example:**
```c
if (fio_filename_make_path(.path = "./build/out/artifacts"))
perror("mkdir -p");
```
### Reading and Writing
#### `fio_fd_write`
```c
FIO_IFUNC ssize_t fio_fd_write(int fd, const void *buf, size_t len);
```
Writes `len` bytes to `fd`, fragmenting the syscall into `write` blocks of up to `1 << 17` bytes. Retries on `EINTR`.
**Returns:** number of bytes written, or `-1` on error. If `fd == -1`, `buf == NULL`, or `len == 0`, returns `-1`.
**Note:** for non-blocking descriptors, check `errno` for `EAGAIN` / `EWOULDBLOCK`.
#### `fio_fd_read`
```c
FIO_IFUNC size_t fio_fd_read(int fd, void *buf, size_t len, off_t start_at);
```
Reads up to `len` bytes from `fd` starting at `start_at`. On POSIX systems with `pread`, the file offset is preserved; otherwise it seeks. Reads are fragmented to `1 << 27` byte chunks. Retries on `EINTR`.
**Parameters:**
- `fd` - file descriptor
- `buf` - destination buffer
- `len` - maximum bytes to read
- `start_at` - offset; negative values seek from end
**Returns:** bytes read, or `0` on error / EOF. Sets `errno = ENOENT` if `fd == -1`, `buf == NULL`, or `len == 0`.
### File Information
#### `fio_filename_size`
```c
FIO_IFUNC size_t fio_filename_size(const char *filename);
```
Returns file size in bytes, or `0` on error or empty file.
#### `fio_fd_size`
```c
FIO_IFUNC size_t fio_fd_size(int fd);
```
Returns file size for an open descriptor, or `0` on error or empty file.
#### `fio_filename_type`
```c
FIO_IFUNC size_t fio_filename_type(const char *filename);
```
Returns the file type bits (`st_mode & S_IFMT`), e.g. `S_IFREG`, `S_IFDIR`, or `0` on error.
#### `fio_fd_type`
```c
FIO_IFUNC size_t fio_fd_type(int fd);
```
Same as `fio_filename_type`, but for an open descriptor.
#### `fio_filename_stat`
```c
FIO_IFUNC int fio_filename_stat(const char *filename, struct stat *stat_buf);
```
Populates `stat_buf` via `stat`. Returns `0` on success, `-1` on error (including `filename == NULL` or `stat_buf == NULL`).
### Path Safety
#### `fio_filename_is_unsafe`
```c
SFUNC int fio_filename_is_unsafe(const char *path);
```
Returns `1` if `path` possibly folds backwards or contains double separators:
- leading `../`
- `/../` or trailing `/..`
- `//`
Uses the OS separator (`\` on Windows, `/` elsewhere). Returns `0` for `NULL` paths.
#### `fio_filename_is_unsafe_url`
```c
SFUNC int fio_filename_is_unsafe_url(const char *path);
```
Same check as `fio_filename_is_unsafe`, but always uses `/` as the separator. Use this for URL paths.
### Filename Parsing
#### `fio_filename_parse`
```c
SFUNC fio_filename_s fio_filename_parse(const char *filename);
```
Splits a NUL-terminated path into folder, basename, and extension without copying.
**Returns:** `fio_filename_s` with pointers into `filename`.
#### `fio_filename_parse2`
```c
SFUNC fio_filename_s fio_filename_parse2(const char *filename, size_t len);
```
Same as `fio_filename_parse`, but reads at most `len` bytes. Use when the input is not NUL-terminated.
### File Search
#### `fio_fd_find_next`
```c
SFUNC size_t fio_fd_find_next(int fd, char token, size_t start_at);
```
Scans `fd` for the next occurrence of `token` starting at `start_at`. Reads in `FIO_FD_FIND_BLOCK` byte chunks.
**Returns:** byte offset of the match, or `FIO_FD_FIND_EOF` if not found.
### Example
```c
#define FIO_FILES
#define FIO_LOG
#include "fio-stl.h"
int main(void) {
fio_filename_s parts = fio_filename_parse("/var/log/app.log");
printf("folder=%.*s base=%.*s ext=%.*s\n",
(int)parts.folder.len, parts.folder.buf,
(int)parts.basename.len, parts.basename.buf,
(int)parts.ext.len, parts.ext.buf);
const char *unsafe = "/var/www/../etc/passwd";
printf("unsafe? %s\n", fio_filename_is_unsafe(unsafe) ? "yes" : "no");
const char *msg = "hello, files";
if (fio_filename_overwrite("/tmp/fio_test.txt", msg, strlen(msg)) == 0) {
printf("wrote %zu bytes\n", strlen(msg));
}
size_t sz = fio_filename_size("/tmp/fio_test.txt");
printf("size = %zu\n", sz);
int tmp = fio_filename_tmp();
if (tmp != -1) {
fio_fd_write(tmp, "temp", 4);
close(tmp);
}
return 0;
}
```
------------------------------------------------------------
# HTTP/1.x Parser
```c
#define FIO_HTTP1_PARSER
#include FIO_INCLUDE_FILE
```
Small parser, sharp teeth. Define `FIO_HTTP1_PARSER` to add the static
HTTP/1.x request / response parser used by the HTTP layer. It performs no heap
allocations, stores only parser state, and reports parsed data through callbacks
implemented by the including translation unit.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the higher-level
[HTTP module header](./439 http.h), and the neighboring
[WebSocket parser header](./004 websocket parser.h).
---
## What Gets Added
`FIO_HTTP1_PARSER` exposes:
- `fio_http1_parser_s` — parser state.
- `FIO_HTTP1_PARSER_INIT` — zero-initializer / reset value.
- `fio_http1_parse` — incremental parser entry point.
- parser state helpers:
- `fio_http1_parser_is_empty`
- `fio_http1_parser_is_on_header`
- `fio_http1_parser_is_on_body`
- `fio_http1_expected`
- `fio_http1_parser_skip_body`
- `fio_http1_parser_skips_body`
- parse result / expected-body constants:
- `FIO_HTTP1_PARSER_ERROR`
- `FIO_HTTP1_EXPECTED_CHUNKED`
- required user callbacks named `fio_http1_on_*`.
The implementation also declares internal parsing stages named with
`fio_http1___...`; these are private implementation details.
---
## Parser State
### `fio_http1_parser_s`
```c
typedef struct fio_http1_parser_s fio_http1_parser_s;
struct fio_http1_parser_s {
int (*fn)(fio_http1_parser_s *, fio_buf_info_s *, void *);
size_t expected;
size_t flags;
};
```
The parser state is intentionally tiny: one function pointer for the current
state-machine stage, one `expected` byte counter / sentinel, and a `flags`
word for per-message state bits (e.g., an accepted `Expect: 100-continue`).
Parser state is never inferred from function pointer identity, so linker
identical-code-folding (MSVC / lld `/OPT:ICF`) cannot alter parsing behavior.
Treat all fields as opaque. Allocate the struct wherever it fits your lifetime
(stack, connection object, arena, etc.), initialize it with
`FIO_HTTP1_PARSER_INIT`, and use the helper functions to inspect state.
### `FIO_HTTP1_PARSER_INIT`
```c
#define FIO_HTTP1_PARSER_INIT ((fio_http1_parser_s){0})
```
Zero-initializes a parser:
```c
fio_http1_parser_s parser = FIO_HTTP1_PARSER_INIT;
```
The parser also resets itself to this empty state after a complete message is
reported with `fio_http1_on_complete`.
---
## Parsing API
### `fio_http1_parse`
```c
FIO_SFUNC size_t fio_http1_parse(fio_http1_parser_s *p,
fio_buf_info_s buf,
void *udata);
```
Parses as much HTTP/1.x data as currently possible and invokes callbacks as
fields are discovered.
- `p` is the parser state.
- `buf` is the current readable bytes.
- `udata` is passed unchanged to every callback.
Returns the number of bytes consumed from `buf`, or
`FIO_HTTP1_PARSER_ERROR` (`(size_t)-1`) on parse / callback error.
A successful return may consume fewer bytes than supplied. Any unconsumed bytes
belong to a later parse step or to the next HTTP message on a keep-alive
connection.
If the parser needs more bytes before it can make progress, it returns
successfully with the bytes consumed so far, which can be `0`.
### State Helpers
```c
FIO_IFUNC size_t fio_http1_parser_is_empty(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_is_on_header(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_is_on_body(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_expected(fio_http1_parser_s *p);
FIO_IFUNC void fio_http1_parser_skip_body(fio_http1_parser_s *p);
FIO_IFUNC size_t fio_http1_parser_skips_body(fio_http1_parser_s *p);
FIO_IFUNC int fio_http1_version_is_legacy(fio_buf_info_s version);
```
- `fio_http1_parser_is_empty` returns non-zero when the parser is waiting for a
new request / response line.
- `fio_http1_parser_is_on_header` returns non-zero while reading regular
headers or chunked trailer headers.
- `fio_http1_parser_is_on_body` returns non-zero while reading a known-length
body or while the chunked parser is ready to read the next chunk frame. During
a split chunk payload, the internal chunk-read stage may report false even
though body bytes are still being drained.
- `fio_http1_expected` returns the parser's current expected byte count, returns
`FIO_HTTP1_EXPECTED_CHUNKED` after a chunked body is detected and before the
next chunk-size line is parsed, and returns `0` when the parser's internal
state marks the message as having no body. During chunked payload delivery,
it may expose the current chunk size / remaining chunk bytes.
- `fio_http1_parser_skip_body` marks the current message as having no body,
regardless of any `content-length` / `transfer-encoding` headers. Call it
from a parser callback before the headers end (normally `on_status`), e.g.,
when parsing the response to a `HEAD` request — the parser can't know the
request method. The mark is cleared when the message completes. See
*Messages Without a Body* below.
- `fio_http1_parser_skips_body` returns non-zero when the current message has
no body. It is known as soon as the first line is parsed (before any header
callback). Test it before reserving body space. See *Messages Without a Body*.
- `fio_http1_version_is_legacy` returns non-zero if a version string (as
passed to `fio_http1_on_version`) is exactly `HTTP/1.0` (case-sensitive).
HTTP/1.0 messages are not persistent without a `keep-alive` connection
option and never use chunked transfer coding (RFC 9112 §9.3, §6.1). Every
other version string gets HTTP/1.1 semantics. HTTP/0.9 is unsupported: its
version-less request line is rejected, and a `HTTP/0.9` version string is
treated as HTTP/1.1 (i.e., `host` is required).
### Constants
```c
#define FIO_HTTP1_PARSER_ERROR ((size_t)-1)
#define FIO_HTTP1_EXPECTED_CHUNKED ((size_t)(-2))
```
`FIO_HTTP1_PARSER_ERROR` is the error return value from `fio_http1_parse`.
After a parse error, close / discard the stream state rather than attempting to
recover the same parser instance.
`FIO_HTTP1_EXPECTED_CHUNKED` is the parser's sentinel after
`transfer-encoding: chunked` is accepted and before a chunk-size line is parsed.
Once chunk parsing starts, `fio_http1_expected` may instead report the current
chunk size or remaining chunk bytes.
The header also defines `FIO___HTTP1_BODY_NOT_ALLOWED` as an internal sentinel
for methods / states where a body should not be read. User code should rely on
`fio_http1_expected(p) == 0` instead of using that internal macro.
---
## Callback Contract
The parser declares these callbacks as `static` prototypes. The including
translation unit must define them.
For portable user code, every `int` callback should return only `0` to continue
or `-1` to reject the parse.
The parser's internal checks are not identical for every callback:
- `fio_http1_on_method`, `fio_http1_on_url`, `fio_http1_on_version`,
`fio_http1_on_status`, and `fio_http1_on_body_chunk` treat any non-zero return
as a parse error.
- `fio_http1_on_header` during normal headers and
`fio_http1_on_header_content_length` reject only an exact `-1` return.
- `fio_http1_on_header` during chunked trailers is passed through; negative
values become parse errors and positive values stop the current parse as
incomplete.
- `fio_http1_on_expect` is special: any non-zero return rejects the expectation,
resets the parser, and stops the current parse without calling
`fio_http1_on_complete`.
All `fio_buf_info_s` values point into the `buf` memory passed to
`fio_http1_parse`. They are not NUL-terminated unless the input happened to be.
Copy or retain the data before the callback returns if it must outlive the input
buffer.
> Important: the parser lowercases header names in-place. Feed it writable
> memory, not a string literal or read-only mapping.
### Completion
```c
static void fio_http1_on_complete(void *udata);
```
Called after the request / response line, headers, and any body have been fully
parsed. The parser is reset before this callback is invoked, so it is ready for
the next message on the same connection.
### Request Line Callbacks
```c
static int fio_http1_on_method(fio_buf_info_s method, void *udata);
static int fio_http1_on_url(fio_buf_info_s path, void *udata);
static int fio_http1_on_version(fio_buf_info_s version, void *udata);
```
For request lines, the parser calls the callbacks in this order:
1. `fio_http1_on_method`
2. `fio_http1_on_url`
3. `fio_http1_on_version`
The version slice is clamped to at most 14 bytes. `GET`, `HEAD`, and `OPTIONS`
are recognized case-insensitively and mark the parser as not expecting a body;
body-bearing headers for these methods conflict with that marker and are
rejected.
### Response Line Callbacks
```c
static int fio_http1_on_version(fio_buf_info_s version, void *udata);
static int fio_http1_on_status(size_t istatus,
fio_buf_info_s status,
void *udata);
```
For response lines, the parser calls `fio_http1_on_version` first and then
`fio_http1_on_status`.
`istatus` is parsed from the numeric status token. `status` is the remaining
status text slice after the numeric token. The version slice is clamped to at
most 14 bytes.
The parser decides whether the first line is a response by checking whether the
second token starts with a decimal digit.
Responses with a `1xx`, `204`, or `304` status are automatically marked as
having no body (see *Messages Without a Body*). The parser does not skip
interim `1xx` responses: each one completes as a separate message, and callers
decide whether to wait for the final response.
### Headers
```c
static int fio_http1_on_header(fio_buf_info_s name,
fio_buf_info_s value,
void *udata);
static int fio_http1_on_header_content_length(fio_buf_info_s name,
fio_buf_info_s value,
size_t content_length,
void *udata);
```
For ordinary headers, `fio_http1_on_header` receives:
- `name` lowercased in-place.
- `value` trimmed of leading and trailing spaces / tabs.
- an empty value as `{ .buf = NULL, .len = 0 }`.
Header names must contain a valid `:` separator and may not contain the
forbidden characters encoded by the parser. NUL bytes in header values are
rejected.
`content-length` is special:
- empty values are rejected;
- non-decimal / overflowing values are rejected;
- values colliding with internal sentinels are rejected;
- duplicate `content-length` headers must agree;
- conflicting `content-length` and final `transfer-encoding: chunked` are
rejected;
- the first non-zero accepted value calls `fio_http1_on_header_content_length`
instead of the generic header callback; a repeated matching value is accepted
without calling the content-length callback again.
A `content-length: 0` value marks the message as having no body and does not
call `fio_http1_on_header_content_length`.
`transfer-encoding` is also special. Its field lines form a single coding list
(RFC 9110 §5.3), and `chunked` (case-insensitive) must be the final coding,
applied exactly once (RFC 9112 §6.3, §7). Anything else can't be framed safely,
so it's rejected rather than reading the body as the next message (request
smuggling):
- the parser switches to chunked body decoding when a field line ends with the
`chunked` token;
- if the value is exactly `chunked`, no generic header callback is made;
- if other transfer-coding text appears before the final `chunked` token, the
final `chunked` token and adjacent separators are stripped before the
remaining value is passed to `fio_http1_on_header` (i.e.,
`gzip, chunked` is framed as chunked and forwards `transfer-encoding: gzip`;
decoding it is up to the parser user, since a `501` response is only a
SHOULD under RFC 9112 §6.1);
- a field line without a final `chunked` token (i.e., `gzip`) is forwarded
unchanged; a later field line may still end the list with `chunked`;
- rejected: a list that doesn't end with `chunked` once the headers end (i.e.,
`gzip`, `identity`, `chunked, gzip`, an empty value, or a `chunked` line
followed by any other `transfer-encoding` line), a repeated `chunked`
coding (`chunked, chunked`), malformed separators before the final
`chunked` token, a `content-length` header, and requests using `GET`,
`HEAD`, or `OPTIONS`.
Requests must carry exactly one `host` header, as required by RFC 9112 §3.2:
- an HTTP/1.1 request without a `host` header is rejected once the headers
end;
- a request with more than one `host` header line is rejected (for any
version);
- `HTTP/1.0` requests may omit `host` (see `fio_http1_version_is_legacy`);
- responses aren't affected. The `host` header itself is passed to
`fio_http1_on_header`.
`expect` is special when its value is exactly `100-continue`. Any other
`Expect` value is rejected.
### Messages Without a Body
The parser identifies bodyless messages from the first line, before any header
callback runs, and reports them through `fio_http1_parser_skips_body`:
- requests using `GET`, `HEAD`, or `OPTIONS` (a non-zero `content-length` on
these is still rejected, as described above);
- responses with a `1xx` / `204` / `304` status (RFC 9112 §6.3);
- messages marked with `fio_http1_parser_skip_body` (i.e., a `HEAD` response,
marked from `on_status`).
`fio_http1_parser_skips_body` is also true after a `content-length: 0` header.
A bodyless response completes at the empty line after its headers, and any
bytes that follow belong to the next message. Body framing is ignored:
- `content-length` is still validated and reported to
`fio_http1_on_header_content_length`, but it is informational. Callbacks must
test `fio_http1_parser_skips_body` before reserving space or enforcing body
size limits (a `HEAD` response may describe a large body that never arrives);
- `transfer-encoding` and `expect` are forwarded unchanged to
`fio_http1_on_header`.
### Expect: 100-continue
```c
static int fio_http1_on_expect(void *udata);
```
Called once, after headers, when an accepted `Expect: 100-continue` header
requires a post-header decision and a body may follow (a `Content-Length` body
or `transfer-encoding: chunked`). It is not called for messages that cannot
carry a body (e.g., `GET` / `HEAD` / `OPTIONS` or a zero `Content-Length`),
since there is nothing to continue (RFC 9110 §10.1.1).
Return `0` to continue into the body / completion flow. Return non-zero to reset
the parser and stop the current parse without calling `fio_http1_on_complete`.
### Body Chunks
```c
static int fio_http1_on_body_chunk(fio_buf_info_s chunk, void *udata);
```
Called with decoded body bytes.
For `Content-Length` bodies, callback chunks follow the supplied input chunks
and sum to the accepted content length.
For chunked bodies, framing bytes are removed before callback delivery. Large or
split HTTP chunks may be delivered through more than one callback if the input
arrives in smaller pieces.
---
## Parser Flow
```text
start line
├─ request -> method -> url -> version
└─ response -> version -> status
headers
├─ no body / body not allowed -> complete (incl. 1xx/204/304, skip_body)
├─ content-length body -> body chunks -> complete
└─ chunked body -> chunk chunks -> trailers -> complete
```
Details worth keeping in mind:
- Leading spaces, `\r`, and `\n` before the first line are skipped.
- First lines shorter than the parser's minimum accepted shape are rejected.
- NUL bytes in the first line are rejected.
- Header and first-line parsing waits for a newline before making progress.
- The parser accepts `\n` line endings and handles an optional preceding `\r`.
- Chunk size lines are hexadecimal, capped by the implementation, and do not
support chunk extensions.
- A zero-size chunk either completes immediately when followed by an empty line
or enters trailer parsing.
---
## Chunked Trailers
Allowed trailer headers are reported through `fio_http1_on_header` after the
body's terminating zero-size chunk.
The parser rejects the following trailer names:
- `authorization`
- `cache-control`
- `content-encoding`
- `content-length`
- `content-range`
- `content-type`
- `expect`
- `host`
- `max-forwards`
- `set-cookie`
- `te`
- `trailer`
- `transfer-encoding`
---
## Ownership and Lifetime
- The parser allocates no memory.
- The parser does not copy callback data.
- The parser mutates header names in the input buffer to lowercase.
- Callback slices are valid only while the input buffer remains valid and
unchanged.
- `udata` is never owned by the parser; it is simply forwarded.
- The parser state may live inside a connection object and be reused for
keep-alive messages. It resets automatically on complete messages.
- After `FIO_HTTP1_PARSER_ERROR`, discard the parser / connection state.
---
## Minimal Skeleton
```c
#define FIO_HTTP1_PARSER
#include FIO_INCLUDE_FILE
static int fio_http1_on_method(fio_buf_info_s method, void *udata) {
(void)method;
(void)udata;
return 0;
}
static int fio_http1_on_url(fio_buf_info_s path, void *udata) {
(void)path;
(void)udata;
return 0;
}
static int fio_http1_on_version(fio_buf_info_s version, void *udata) {
(void)version;
(void)udata;
return 0;
}
static int fio_http1_on_status(size_t status_code,
fio_buf_info_s status,
void *udata) {
(void)status_code;
(void)status;
(void)udata;
return 0;
}
static int fio_http1_on_header(fio_buf_info_s name,
fio_buf_info_s value,
void *udata) {
(void)name;
(void)value;
(void)udata;
return 0;
}
static int fio_http1_on_header_content_length(fio_buf_info_s name,
fio_buf_info_s value,
size_t content_length,
void *udata) {
(void)name;
(void)value;
(void)udata;
return content_length > (1UL << 20) ? -1 : 0;
}
static int fio_http1_on_expect(void *udata) {
(void)udata;
return 0;
}
static int fio_http1_on_body_chunk(fio_buf_info_s chunk, void *udata) {
(void)chunk;
(void)udata;
return 0;
}
static void fio_http1_on_complete(void *udata) {
(void)udata;
}
size_t parse_some_http(char *data, size_t len, void *udata) {
fio_http1_parser_s parser = FIO_HTTP1_PARSER_INIT;
return fio_http1_parse(&parser, FIO_BUF_INFO2(data, len), udata);
}
```
For real incremental parsing, keep `fio_http1_parser_s` with the connection and
preserve / retry unconsumed bytes when `fio_http1_parse` returns less than the
available buffer length.
# JSON Parser
```c
#define FIO_JSON
#include "fio-stl.h"
```
A non-strict, streaming, callback-based JSON parser. It does not build a tree for you; it calls your callbacks for each value and lets you decide what to construct. Implemented in [`./004 json.h`](https://facil.io/edge/api/004-json/). Depends on `FIO_ATOL`, which is included automatically.
The parser tolerates trailing commas, comments (`//`, `/* */`, `#`), newlines inside strings, hex/octal/binary numbers, `NaN`, and `Infinity`.
### Configuration Macros
#### `FIO_JSON_MAX_DEPTH`
```c
#ifndef FIO_JSON_MAX_DEPTH
#define FIO_JSON_MAX_DEPTH 128
#endif
```
Maximum JSON nesting depth. Must be less than `65536`. Deeper input aborts with an error.
#### `FIO_JSON_USE_FIO_ATON`
```c
#ifndef FIO_JSON_USE_FIO_ATON
#define FIO_JSON_USE_FIO_ATON 0
#endif
```
Set to `1` to use `fio_aton` for number parsing instead of the built-in logic.
### Types
#### `fio_json_parser_callbacks_s`
```c
typedef struct {
void *(*on_null)(void *udata);
void *(*on_true)(void *udata);
void *(*on_false)(void *udata);
void *(*on_number)(void *udata, int64_t i);
void *(*on_float)(void *udata, double f);
void *(*on_string)(void *udata, const void *start, size_t len);
void *(*on_string_simple)(void *udata, const void *start, size_t len);
void *(*on_map)(void *udata, void *ctx, void *at);
void *(*on_array)(void *udata, void *ctx, void *at);
int (*map_push)(void *udata, void *ctx, void *key, void *value);
int (*array_push)(void *udata, void *ctx, void *value);
int (*array_finished)(void *udata, void *ctx);
int (*map_finished)(void *udata, void *ctx);
int (*is_array)(void *udata, void *ctx);
int (*is_map)(void *udata, void *ctx);
void (*free_unused_object)(void *udata, void *ctx);
void *(*on_error)(void *udata, void *ctx);
} fio_json_parser_callbacks_s;
```
Callback table for the parser. All callbacks receive `udata` as their first argument, so the same `static const` table can be shared across concurrent calls.
**Required callbacks:** `on_null`, `on_true`, `on_false`, `on_number`, `on_float`, `on_string` (or `on_string_simple`), `on_map`, `on_array`, `map_push`, `array_push`, `free_unused_object`.
**Optional callbacks:** `on_string_simple`, `array_finished`, `map_finished`, `is_array`, `is_map`, `on_error`. Missing optional callbacks default to no-ops. If only one of `on_string` / `on_string_simple` is provided, the other uses it as a fallback.
**Ownership:** callbacks that return `void *` transfer ownership to the parser. The parser passes objects to `map_push` / `array_push` or calls `free_unused_object` for discarded objects. `on_error` receives ownership of any partial result.
#### `fio_json_result_s`
```c
typedef struct {
void *ctx;
size_t stop_pos;
int err;
} fio_json_result_s;
```
Parse result.
**Members:**
- `ctx` - root object returned by callbacks, or `NULL`
- `stop_pos` - number of bytes consumed from the input
- `err` - non-zero if parsing stopped because of an error
### API Functions
#### `fio_json_parse`
```c
SFUNC fio_json_result_s fio_json_parse(fio_json_parser_callbacks_s *settings,
void *udata,
const char *json_string,
const size_t len);
```
Parses `json_string` up to `len` bytes. Stops as soon as a complete top-level value is consumed or an error occurs.
**Parameters:**
- `settings` - callback table
- `udata` - per-call user data threaded through every callback
- `json_string` - input buffer
- `len` - input length in bytes
**Returns:** `fio_json_result_s` with the root context, bytes consumed, and error flag.
**Note:** a UTF-8 BOM at the start of the buffer is skipped automatically.
**Guard-byte contract:** number and quote-less key scanning may read the byte at `json_string[len]` while deciding a token ended (see the `fio_atol*` guard-byte contract). Callers MUST keep that byte readable and non-numeric - pass a NUL-terminated string (any `fio_bstr` string qualifies) or append a non-numeric guard byte. Exact-size buffers with no readable byte past `len` violate the contract and will trigger AddressSanitizer.
#### `fio_json_parse_update`
```c
SFUNC fio_json_result_s fio_json_parse_update(fio_json_parser_callbacks_s *s,
void *udata,
void *ctx,
const char *start,
const size_t len);
```
Merges JSON data into an existing object (`ctx`). The input must be wrapped in an aggregate of the same type as `ctx`. Requires `is_array` and `is_map` callbacks.
**Parameters:**
- `s` - callback table
- `udata` - per-call user data
- `ctx` - existing object to update
- `start` - input buffer
- `len` - input length
**Returns:** parse result with the (possibly updated) context.
### Example
```c
#define FIO_JSON
#define FIO_LOG
#include "fio-stl.h"
typedef enum { T_NULL, T_TRUE, T_FALSE, T_NUM, T_STR, T_ARR, T_MAP } type_e;
typedef struct obj_s {
type_e type;
union {
int64_t i;
struct { char *buf; size_t len; } s;
struct { struct obj_s **items; size_t len; } a;
struct { struct obj_s **keys; struct obj_s **vals; size_t len; } m;
};
} obj_s;
static obj_s *mk(type_e t) {
obj_s *o = calloc(1, sizeof(*o));
o->type = t;
return o;
}
static void *on_null(void *u) { (void)u; return mk(T_NULL); }
static void *on_true(void *u) { (void)u; return mk(T_TRUE); }
static void *on_false(void *u) { (void)u; return mk(T_FALSE); }
static void *on_number(void *u, int64_t i) { (void)u; obj_s *o = mk(T_NUM); o->i = i; return o; }
static void *on_string(void *u, const void *s, size_t l) {
(void)u; obj_s *o = mk(T_STR); o->s.buf = malloc(l+1); memcpy(o->s.buf, s, l); o->s.buf[l] = 0; o->s.len = l; return o;
}
static void *on_array(void *u, void *c, void *a) { (void)u; (void)c; (void)a; return mk(T_ARR); }
static void *on_map(void *u, void *c, void *a) { (void)u; (void)c; (void)a; return mk(T_MAP); }
static int array_push(void *u, void *c, void *v) {
(void)u; obj_s *a = c; a->a.items = realloc(a->a.items, (a->a.len+1)*sizeof(*a->a.items)); a->a.items[a->a.len++] = v; return 0;
}
static int map_push(void *u, void *c, void *k, void *v) {
(void)u; obj_s *m = c;
m->m.keys = realloc(m->m.keys, (m->m.len+1)*sizeof(*m->m.keys));
m->m.vals = realloc(m->m.vals, (m->m.len+1)*sizeof(*m->m.vals));
m->m.keys[m->m.len] = k; m->m.vals[m->m.len] = v; m->m.len++; return 0;
}
static void free_obj(void *u, void *c) { (void)u; free(c); }
int main(void) {
static const fio_json_parser_callbacks_s cb = {
.on_null = on_null, .on_true = on_true, .on_false = on_false,
.on_number = on_number, .on_string = on_string, .on_string_simple = on_string,
.on_array = on_array, .on_map = on_map,
.array_push = array_push, .map_push = map_push,
.free_unused_object = free_obj,
};
const char *json = "{ \"n\": 42, \"s\": \"hi\", \"a\": [1, 2] }";
fio_json_result_s r = fio_json_parse(&cb, NULL, json, strlen(json));
if (r.err) {
FIO_LOG_ERROR("parse error at %zu", r.stop_pos);
return 1;
}
printf("parsed %zu bytes, root ctx %p\n", r.stop_pos, r.ctx);
return 0;
}
```
------------------------------------------------------------
# MIME Multipart Parser
```c
#define FIO_MULTIPART
#include "fio-stl.h"
```
A non-allocating, streaming, callback-based `multipart/form-data` parser. It finds parts, extracts `Content-Disposition` and `Content-Type`, then calls your callbacks for each field or file chunk. Implemented in [`./004 multipart.h`](https://facil.io/edge/api/004-multipart/).
The parser does not decode transfer encodings or unescape headers — it just slices the original buffer into `fio_buf_info_s` ranges.
### Types
#### `fio_multipart_parser_callbacks_s`
```c
typedef struct {
void *(*on_field)(void *udata,
fio_buf_info_s name,
fio_buf_info_s value,
fio_buf_info_s content_type);
void *(*on_field_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s content_type);
int (*on_field_data)(void *udata, void *field_ctx, fio_buf_info_s data);
void (*on_field_end)(void *udata, void *field_ctx);
void *(*on_file_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s filename,
fio_buf_info_s content_type);
int (*on_file_data)(void *udata, void *file_ctx, fio_buf_info_s data);
void (*on_file_end)(void *udata, void *file_ctx);
void (*on_error)(void *udata);
} fio_multipart_parser_callbacks_s;
```
Callback table. All callbacks are optional; unset ones become no-ops.
**Members:**
- `on_field` - called for regular form fields (no `filename`). Ignored if `on_field_start` is provided.
- `on_field_start` - optional streaming field start. If set, `on_field` is ignored and `on_field_data` / `on_field_end` are used instead.
- `on_field_data` - called with a field data chunk. Return non-zero to abort.
- `on_field_end` - called when a streaming field ends.
- `on_file_start` - called when a file upload starts. Returns a context pointer for this file.
- `on_file_data` - called with a file data chunk. Return non-zero to abort.
- `on_file_end` - called when a file upload ends.
- `on_error` - called on parse error (optional).
#### `fio_multipart_result_s`
```c
typedef struct {
size_t consumed;
size_t field_count;
size_t file_count;
int err;
} fio_multipart_result_s;
```
Parse result.
**Members:**
- `consumed` - bytes consumed from the input buffer
- `field_count` - regular form fields parsed in this call
- `file_count` - file uploads parsed in this call
- `err` - `0` success, `-1` error, `-2` need more data
### API Functions
#### `fio_multipart_parse`
```c
SFUNC fio_multipart_result_s
fio_multipart_parse(const fio_multipart_parser_callbacks_s *callbacks,
void *udata,
fio_buf_info_s boundary,
const char *data,
size_t len);
```
Parses `data` as MIME multipart input. `boundary` is the boundary string **without** the leading `"--"` and must not exceed 250 bytes.
**Parameters:**
- `callbacks` - callback table (typically `static const`)
- `udata` - user data passed to every callback
- `boundary` - boundary value from the `Content-Type` header
- `data` - input buffer
- `len` - input length in bytes
**Returns:** parse result.
**Streaming behavior:**
- `err == 0` - parsing complete; closing boundary found.
- `err == -2` - need more data. Keep `data + result.consumed ... data + len` and append new data before calling again.
- `err == -1` - unrecoverable parse error or callback abort.
### Examples
#### Basic field and file parsing
```c
#define FIO_MULTIPART
#include "fio-stl.h"
static void *on_field(void *u, fio_buf_info_s n, fio_buf_info_s v, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("field %.*s = %.*s\n", (int)n.len, n.buf, (int)v.len, v.buf);
return NULL;
}
static void *on_file_start(void *u, fio_buf_info_s n, fio_buf_info_s fn, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("file %.*s / %.*s\n", (int)n.len, n.buf, (int)fn.len, fn.buf);
return NULL;
}
static int on_file_data(void *u, void *c, fio_buf_info_s d) {
(void)u; (void)c;
printf(" chunk %zu bytes\n", d.len);
return 0;
}
static void on_file_end(void *u, void *c) { (void)u; (void)c; }
int main(void) {
static const fio_multipart_parser_callbacks_s cb = {
.on_field = on_field,
.on_file_start = on_file_start,
.on_file_data = on_file_data,
.on_file_end = on_file_end,
};
const char *body =
"--B\r\n"
"Content-Disposition: form-data; name=\"x\"\r\n\r\n"
"hello\r\n"
"--B\r\n"
"Content-Disposition: form-data; name=\"f\"; filename=\"t.txt\"\r\n"
"Content-Type: text/plain\r\n\r\n"
"data\r\n"
"--B--\r\n";
fio_multipart_result_s r = fio_multipart_parse(
&cb, NULL, FIO_BUF_INFO2("B", 1), body, strlen(body));
printf("fields=%zu files=%zu err=%d\n", r.field_count, r.file_count, r.err);
return 0;
}
```
#### Streaming field callbacks
```c
static void *field_start(void *u, fio_buf_info_s n, fio_buf_info_s ct) {
(void)u; (void)ct;
printf("field start %.*s\n", (int)n.len, n.buf);
return NULL;
}
static int field_data(void *u, void *c, fio_buf_info_s d) {
(void)u; (void)c;
printf(" field chunk %zu bytes\n", d.len);
return 0;
}
static void field_end(void *u, void *c) { (void)u; (void)c; }
static const fio_multipart_parser_callbacks_s cb_stream = {
.on_field_start = field_start,
.on_field_data = field_data,
.on_field_end = field_end,
};
```
------------------------------------------------------------
# RESP3 Parser
```c
#define FIO_RESP3
#include "fio-stl.h"
```
A streaming, callback-based [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) parser for Redis 6+ and compatible servers. Implemented in [`./004 resp3.h`](https://facil.io/edge/api/004-resp3/). Depends on `FIO_ATOL`, included automatically.
The parser pushes values through a small internal stack, calling your callbacks for primitives, containers, and streaming strings. It does not allocate on its own.
### Configuration Macros
#### `FIO_RESP3_MAX_NESTING`
```c
#ifndef FIO_RESP3_MAX_NESTING
#define FIO_RESP3_MAX_NESTING 32
#endif
```
Maximum nesting depth. Valid range is 2 to 32,768. Determines the size of `fio_resp3_parser_s.stack`.
#### `FIO_RESP3_STREAM_THRESHOLD`
```c
#ifndef FIO_RESP3_STREAM_THRESHOLD
#define FIO_RESP3_STREAM_THRESHOLD 4096
#endif
```
Fixed-length blob strings (`$`, `!`, `=`) larger than this threshold are **streamed incrementally** when the callback table provides the streaming string callbacks (`on_start_string`, `on_string_write`, `on_string_done`): the parser starts the string as soon as its header is parsed and feeds data in chunks as it arrives (`result.consumed` advances per chunk), instead of waiting for the whole blob to be contiguous in the read window. This allows blobs larger than any consumer read buffer.
Blobs at or below the threshold keep the wait-for-contiguity behavior (a single zero-copy `on_string_write`, even across split reads). Callback tables without streaming callbacks are unaffected (they always wait for contiguity).
### RESP3 Type Constants
```c
#define FIO_RESP3_SIMPLE_STR '+' /* +\r\n */
#define FIO_RESP3_SIMPLE_ERR '-' /* -\r\n */
#define FIO_RESP3_NUMBER ':' /* :\r\n */
#define FIO_RESP3_NULL '_' /* _\r\n */
#define FIO_RESP3_DOUBLE ',' /* ,\r\n */
#define FIO_RESP3_BOOL '#' /* #t\r\n or #f\r\n */
#define FIO_RESP3_BIGNUM '(' /* (\r\n */
#define FIO_RESP3_BLOB_STR '$' /* $\r\n\r\n */
#define FIO_RESP3_BLOB_ERR '!' /* !\r\n\r\n */
#define FIO_RESP3_VERBATIM '=' /* =\r\n\r\n */
#define FIO_RESP3_ARRAY '*' /* *\r\n... */
#define FIO_RESP3_MAP '%' /* %\r\n... */
#define FIO_RESP3_SET '~' /* ~\r\n... */
#define FIO_RESP3_PUSH '>' /* >\r\n... */
#define FIO_RESP3_ATTR '|' /* |\r\n... */
#define FIO_RESP3_STREAM_CHUNK ';' /* ;\r\n\r\n */
#define FIO_RESP3_STREAM_END '.' /* .\r\n */
```
### Types
#### `fio_resp3_frame_s`
```c
typedef struct {
void *ctx;
void *key;
int64_t remaining;
uint8_t type;
uint8_t streaming;
uint8_t expecting_value;
uint8_t set_as_map;
} fio_resp3_frame_s;
```
Internal stack frame for a nested container. You do not need to touch this directly, but it is exposed in `fio_resp3_parser_s`.
#### `fio_resp3_parser_s`
```c
typedef struct {
void *udata;
uint32_t depth;
uint8_t error;
uint8_t streaming_string;
uint8_t streaming_string_type;
uint8_t streaming_blob_crlf;
void *streaming_string_ctx;
int64_t streaming_remaining;
fio_resp3_frame_s stack[FIO_RESP3_MAX_NESTING];
} fio_resp3_parser_s;
```
Parser state. Initialize with `{.udata = my_context}` before the first call and reuse it for continuation after partial parses.
**Members:**
- `udata` - user data passed to all callbacks
- `depth` - current nesting depth
- `error` - set to non-zero after a protocol error; the parser will refuse further input
- `streaming_string` - non-zero while a streamed string (`$?` chunked, or a fixed-length blob above `FIO_RESP3_STREAM_THRESHOLD`) is in progress
- `streaming_string_type` - type of the streaming string in progress
- `streaming_blob_crlf` - trailing CRLF bytes pending for a streamed fixed-length blob (0...2)
- `streaming_string_ctx` - context returned by `on_start_string`
- `streaming_remaining` - data bytes remaining for a streamed fixed-length blob (0 when inactive or in `$?` chunked mode)
- `stack` - nested container frames
#### `fio_resp3_callbacks_s`
```c
typedef struct {
/* primitives */
void *(*on_null)(void *udata);
void *(*on_bool)(void *udata, int is_true);
void *(*on_number)(void *udata, int64_t num);
void *(*on_double)(void *udata, double num);
void *(*on_bignum)(void *udata, const void *data, size_t len);
void *(*on_string)(void *udata, const void *data, size_t len, uint8_t type);
void *(*on_error)(void *udata, const void *data, size_t len, uint8_t type);
/* containers */
void *(*on_array)(void *udata, void *parent_ctx, int64_t len);
void *(*on_map)(void *udata, void *parent_ctx, int64_t len);
void *(*on_set)(void *udata, void *parent_ctx, int64_t len);
void *(*on_push)(void *udata, void *parent_ctx, int64_t len);
void *(*on_attr)(void *udata, void *parent_ctx, int64_t len);
/* push into containers */
int (*array_push)(void *udata, void *ctx, void *value);
int (*map_push)(void *udata, void *ctx, void *key, void *value);
int (*set_push)(void *udata, void *ctx, void *value);
int (*push_push)(void *udata, void *ctx, void *value);
int (*attr_push)(void *udata, void *ctx, void *key, void *value);
/* finalize containers */
void *(*array_done)(void *udata, void *ctx);
void *(*map_done)(void *udata, void *ctx);
void *(*set_done)(void *udata, void *ctx);
void *(*push_done)(void *udata, void *ctx);
void *(*attr_done)(void *udata, void *ctx);
/* errors */
void (*free_unused)(void *udata, void *obj);
void *(*on_error_protocol)(void *udata);
/* streaming strings */
void *(*on_start_string)(void *udata, size_t len, uint8_t type);
int (*on_string_write)(void *udata, void *ctx, const void *data, size_t len);
void *(*on_string_done)(void *udata, void *ctx, uint8_t type);
} fio_resp3_callbacks_s;
```
Callback table. Designed to be `static const`. Non-streaming callbacks left `NULL` are replaced with safe no-ops. Primitive no-ops return a non-NULL sentinel; container no-ops do the same; push no-ops return `0`; done no-ops return the context unchanged. The streaming-string callbacks (`on_start_string`, `on_string_write`, and `on_string_done`) are not filled with no-op fallbacks.
If `on_set`, `set_push`, and `set_done` are all `NULL` but map callbacks are provided, the parser treats RESP3 Sets as Maps, calling `map_push(ctx, value, value)` and `map_done`.
#### `fio_resp3_result_s`
```c
typedef struct {
void *obj;
size_t consumed;
int err;
} fio_resp3_result_s;
```
Parse result.
**Members:**
- `obj` - top-level object, or `NULL` while incomplete / on error
- `consumed` - bytes consumed from the buffer
- `err` - non-zero if a protocol error occurred
### API Functions
#### `fio_resp3_parse`
```c
SFUNC fio_resp3_result_s fio_resp3_parse(fio_resp3_parser_s *parser,
const fio_resp3_callbacks_s *callbacks,
const void *buf,
size_t len);
```
Parses as much RESP3 data as possible. State is preserved in `parser`, so you can call again with the remaining bytes after a partial read.
**Parameters:**
- `parser` - parser state
- `callbacks` - callback table (may be `NULL`; non-streaming callbacks become no-ops, while streaming-string callbacks remain unset)
- `buf` - input buffer
- `len` - input length
**Returns:** parse result.
**Note:** streamed strings (`$?`) require `on_start_string`, `on_string_write`, and `on_string_done` to be usable together. If `on_start_string` is missing or returns `NULL`, the parser sets `err` because it cannot buffer an unknown-length value. Once `on_start_string` returns a context, the parser enters streaming-string mode and later calls `on_string_write` and `on_string_done` directly, so those callbacks must also be supplied. For fixed-length blob, blob-error, and verbatim strings, `on_start_string` is optional; if it is missing or returns `NULL`, the parser falls back to `on_string` or `on_error`. If it returns a context for a fixed-length string, `on_string_write` and `on_string_done` are also called directly. Additionally, fixed-length blobs larger than `FIO_RESP3_STREAM_THRESHOLD` are always streamed incrementally when `on_start_string` is available (multiple `on_string_write` calls across parse calls possible), while smaller blobs complete in a single write once contiguous.
**Note:** top-level attributes (`|`) are delivered via callbacks but do not become the returned `obj`; parsing continues for the following reply.
### Example
```c
#define FIO_RESP3
#include "fio-stl.h"
static void *on_null(void *u) { (void)u; return (void *)1; }
static void *on_number(void *u, int64_t n) { (void)u; printf("num %lld\n", (long long)n); return (void *)1; }
static void *on_string(void *u, const void *d, size_t l, uint8_t t) {
(void)u; (void)t;
printf("str %.*s\n", (int)l, (const char *)d);
return (void *)1;
}
static void *on_array(void *u, void *c, int64_t l) { (void)u; (void)c; (void)l; return (void *)2; }
static int array_push(void *u, void *c, void *v) { (void)u; (void)c; (void)v; return 0; }
static void *array_done(void *u, void *c) { (void)u; return c; }
int main(void) {
static const fio_resp3_callbacks_s cb = {
.on_null = on_null, .on_number = on_number, .on_string = on_string,
.on_array = on_array, .array_push = array_push, .array_done = array_done,
};
const char *data = "*2\r\n:42\r\n$5\r\nhello\r\n";
fio_resp3_parser_s p = {0};
fio_resp3_result_s r = fio_resp3_parse(&p, &cb, data, strlen(data));
printf("consumed=%zu err=%d obj=%p\n", r.consumed, r.err, r.obj);
return 0;
}
```
------------------------------------------------------------
# Basic Socket Helpers
```c
#define FIO_SOCK
#include "fio-stl.h"
```
Portable socket helpers for POSIX and Windows: open, bind, connect, listen, wait, read, write, close, and a few sharp socket-shaped knives. Implemented in [`./004 sock.h`](https://facil.io/edge/api/004-sock/).
`FIO_SOCK` depends on URL parsing internally and pulls in what it needs.
### Configuration Macros
#### `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`
```c
#ifndef FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT
#define FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT (1ULL << 24)
#endif
```
Default target used by `fio_sock_maximize_limits(0)`.
#### `FIO_SOCK_AVOID_UMASK`
```c
/* optional compile-time flag */
#define FIO_SOCK_AVOID_UMASK
```
If defined before including the implementation, Unix socket creation avoids the temporary `umask` call used when making public Unix sockets. This avoids `umask`'s process-global race, but may affect permissions on systems where `chmod` on Unix socket files is not enough.
Windows behaves as if this concern does not apply.
### Types and Flags
#### `fio_socket_i`
```c
#if FIO_OS_WIN
typedef SOCKET fio_socket_i;
#else
typedef int fio_socket_i;
#endif
```
Native socket handle type. Use this instead of `int` when writing portable code.
#### `FIO_SOCKET_INVALID`
```c
#if FIO_OS_WIN
#define FIO_SOCKET_INVALID INVALID_SOCKET
#else
#define FIO_SOCKET_INVALID (-1)
#endif
```
Invalid socket sentinel.
#### `FIO_SOCK_FD_ISVALID`
```c
#define FIO_SOCK_FD_ISVALID(fd) ((fio_socket_i)(fd) != FIO_SOCKET_INVALID)
```
Returns non-zero if `fd` is not the invalid sentinel.
#### `fio_sock_open_flags_e`
```c
typedef enum {
FIO_SOCK_SERVER = 0,
FIO_SOCK_CLIENT = 1,
FIO_SOCK_NONBLOCK = 2,
FIO_SOCK_TCP = 4,
FIO_SOCK_UDP = 8,
#ifdef AF_UNIX
FIO_SOCK_UNIX = 16,
FIO_SOCK_UNIX_PRIVATE = (16 | 32),
#else
#define FIO_SOCK_UNIX 0
#define FIO_SOCK_UNIX_PRIVATE 0
#endif
} fio_sock_open_flags_e;
```
Flags for `fio_sock_open`, `fio_sock_open2`, and `fio_sock_open_unix`.
**Values:**
- `FIO_SOCK_SERVER` - bind a local socket; TCP / stream sockets also call `listen`
- `FIO_SOCK_CLIENT` - connect a remote socket
- `FIO_SOCK_NONBLOCK` - set non-blocking mode
- `FIO_SOCK_TCP` - stream socket
- `FIO_SOCK_UDP` - datagram socket
- `FIO_SOCK_UNIX` - Unix domain socket where supported, otherwise `0`
- `FIO_SOCK_UNIX_PRIVATE` - Unix socket with private permissions where supported, otherwise `0`
`FIO_SOCK_TCP` and `FIO_SOCK_UDP` are exclusive. If neither is set, network sockets default to TCP.
### Opening Sockets
#### `fio_sock_open`
```c
FIO_IFUNC fio_socket_i fio_sock_open(const char *restrict address,
const char *restrict port,
uint16_t flags);
```
Creates a socket from an address / port pair and flags.
For `FIO_SOCK_UNIX`, `port` is ignored and `address` is treated as the Unix socket path. For server TCP sockets, `address == NULL` binds to all interfaces.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID` on error.
#### `fio_sock_open2`
```c
SFUNC fio_socket_i fio_sock_open2(const char *url, uint16_t flags);
```
Creates a socket from a URL-ish string. If no port is present, the URL scheme is used as the port / service name. Schemes such as `tcp`, `udp`, `http`, and `https` can also imply socket type when flags omit it.
Unix path URLs are detected when supported. A `priv` scheme selects `FIO_SOCK_UNIX_PRIVATE`; other path-only Unix forms select `FIO_SOCK_UNIX`.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID` on error.
#### `fio_sock_address_new`
```c
FIO_IFUNC struct addrinfo *fio_sock_address_new(const char *restrict address,
const char *restrict port,
int sock_type);
```
Resolves `address` / `port` into an `addrinfo` list for `sock_type`, usually `SOCK_STREAM` or `SOCK_DGRAM`.
Common service names are normalized before `getaddrinfo`: `ws` / `http` / `sse` to port `80`, and `wss` / `https` / `sses` to port `443`.
**Ownership:** free the result with `fio_sock_address_free`.
#### `fio_sock_address_free`
```c
FIO_IFUNC void fio_sock_address_free(struct addrinfo *a);
```
Frees an address list returned by `fio_sock_address_new`.
#### `fio_sock_open_local`
```c
SFUNC fio_socket_i fio_sock_open_local(struct addrinfo *addr, int nonblock);
```
Creates a network socket from an `addrinfo` list and binds it locally. `SO_REUSEADDR` is enabled. If `nonblock` is non-zero, non-blocking mode is requested before binding.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID`.
#### `fio_sock_open_remote`
```c
SFUNC fio_socket_i fio_sock_open_remote(struct addrinfo *addr, int nonblock);
```
Creates a network socket from an `addrinfo` list and connects it remotely. Non-blocking connect progress (`EINPROGRESS` / Windows equivalents) returns the socket.
**Returns:** a socket handle, or `FIO_SOCKET_INVALID`.
#### `fio_sock_open_unix`
```c
SFUNC fio_socket_i fio_sock_open_unix(const char *address, uint16_t flags);
```
Creates a Unix domain socket where supported. Server sockets unlink an existing path before binding. Non-UDP server sockets call `listen`.
If Unix sockets are unsupported, the function asserts and returns `FIO_SOCKET_INVALID`.
### Portable Socket Operations
Use these wrappers when code should run on both POSIX and WinSock2.
#### `fio_sock_write`
```c
FIO_IFUNC ssize_t fio_sock_write(fio_socket_i fd, const void *buf, size_t len);
```
Acts like POSIX `write` on POSIX and uses `send` on Windows.
#### `fio_sock_read`
```c
FIO_IFUNC ssize_t fio_sock_read(fio_socket_i fd, void *buf, size_t len);
```
Acts like POSIX `read` on POSIX and uses `recv` on Windows.
#### `fio_sock_sendto`
```c
FIO_IFUNC ssize_t fio_sock_sendto(fio_socket_i fd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `sendto` wrapper.
#### `fio_sock_recvfrom`
```c
FIO_IFUNC ssize_t fio_sock_recvfrom(fio_socket_i fd,
void *buf,
size_t len,
int flags,
struct sockaddr *addr,
socklen_t *addrlen);
```
Portable `recvfrom` wrapper.
#### `fio_sock_close`
```c
FIO_IFUNC int fio_sock_close(fio_socket_i fd);
```
Portable close wrapper: `close` on POSIX, `closesocket` on Windows.
#### `fio_sock_accept`
```c
#if FIO_OS_WIN
IFUNC fio_socket_i fio_sock_accept(fio_socket_i s,
struct sockaddr *addr,
int *addrlen);
#else
#define fio_sock_accept(fd, addr, addrlen) accept(fd, addr, addrlen)
#endif
```
Portable accept wrapper.
#### `fio_sock_dup`
```c
FIO_IFUNC fio_socket_i fio_sock_dup(fio_socket_i fd);
```
Duplicates a socket handle. POSIX uses `dup` and sets `FD_CLOEXEC`; Windows uses `WSADuplicateSocket` and `WSASocket`.
#### `fio_sock_bind`
```c
FIO_IFUNC int fio_sock_bind(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `bind` wrapper.
#### `fio_sock_connect`
```c
FIO_IFUNC int fio_sock_connect(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);
```
Portable `connect` wrapper.
#### `fio_sock_listen`
```c
FIO_IFUNC int fio_sock_listen(fio_socket_i fd, int backlog);
```
Portable `listen` wrapper.
#### `fio_sock_setsockopt`
```c
FIO_IFUNC int fio_sock_setsockopt(fio_socket_i fd,
int level,
int optname,
const void *optval,
socklen_t optlen);
```
Portable `setsockopt` wrapper. Pointer and length type differences are handled internally.
#### `fio_sock_socketpair`
```c
FIO_IFUNC int fio_sock_socketpair(fio_socket_i fds[2]);
```
Creates a connected socket pair. POSIX uses `socketpair(AF_UNIX, SOCK_STREAM, 0, fds)`. Windows creates a loopback TCP pair.
#### `fio_sock_pipe`
```c
FIO_IFUNC int fio_sock_pipe(fio_socket_i fds[2]);
```
Creates a pipe-like pair. POSIX uses `pipe`; Windows delegates to `fio_sock_socketpair` so the handles are usable with WinSock polling.
### Socket State and Waiting
#### `fio_sock_set_non_block`
```c
SFUNC int fio_sock_set_non_block(fio_socket_i fd);
```
Sets a socket to non-blocking mode.
**Returns:** `0` on success, `-1` on error.
#### `fio_sock_maximize_limits`
```c
SFUNC size_t fio_sock_maximize_limits(size_t maximum_limit);
```
Attempts to raise the open-file limit up to `maximum_limit`. Passing `0` uses `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`.
On Windows this returns the advisory cap because there is no `RLIMIT_NOFILE` equivalent for sockets.
#### `fio_sock_wait_io`
```c
SFUNC short fio_sock_wait_io(fio_socket_i fd, short events, int timeout);
```
Waits for `events` on one socket using `poll` / `WSAPoll`. `timeout` is in milliseconds; `0` returns immediately.
**Returns:** `0` on timeout, `-1` on error, or event bits such as `POLLIN`, `POLLOUT`, `POLLHUP`, and `POLLNVAL`.
#### `FIO_SOCK_WAIT_RW`
```c
#define FIO_SOCK_WAIT_RW(fd, timeout_) \
fio_sock_wait_io(fd, POLLIN | POLLOUT, timeout_)
```
Waits for read or write readiness.
#### `FIO_SOCK_WAIT_R`
```c
#define FIO_SOCK_WAIT_R(fd, timeout_) fio_sock_wait_io(fd, POLLIN, timeout_)
```
Waits for read readiness.
#### `FIO_SOCK_WAIT_W`
```c
#define FIO_SOCK_WAIT_W(fd, timeout_) fio_sock_wait_io(fd, POLLOUT, timeout_)
```
Waits for write readiness.
#### `FIO_SOCK_IS_OPEN`
```c
#ifdef POLLRDHUP
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, (POLLOUT | POLLRDHUP), 0) & \
(POLLRDHUP | POLLHUP | POLLNVAL)))
#else
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, POLLOUT, 0) & (POLLHUP | POLLNVAL)))
#endif
```
Best-effort test for whether a socket still appears open.
#### `fio_sock_peer_addr`
```c
SFUNC fio_buf_info_s fio_sock_peer_addr(fio_socket_i s);
```
Returns a numeric, human-readable peer address.
On error, returns `{NULL, 0}`. The returned buffer is internal static storage, limited to 63 bytes. Thread-safety is limited to 128 threads / calls.
### Windows Notes
On Windows, the module initializes WinSock in a constructor and stores direct function pointers loaded from `Ws2_32.dll`. Wrapper functions translate common WinSock errors into `errno` values.
The `fio___sock_wsa*` helpers are internal implementation details, not regular application API. Tiny dragons; leave them napping.
### Ownership and Thread-Safety
Socket handles are owned by the caller and should be closed with `fio_sock_close`. `fio_sock_address_new` results are owned by the caller and should be freed with `fio_sock_address_free`.
Socket operations are as thread-safe as the operating system calls they wrap. `fio_sock_peer_addr` uses shared static buffers and has the 128-call/thread caveat described above. Unix socket creation may touch process-global `umask` unless `FIO_SOCK_AVOID_UMASK` is defined.
### Example
```c
#define FIO_SOCK
#include "fio-stl.h"
#include
#include
int main(void) {
fio_socket_i fds[2];
char buf[8] = {0};
if (fio_sock_pipe(fds))
return 1;
fio_sock_write(fds[1], "ping", 4);
fio_sock_read(fds[0], buf, sizeof(buf) - 1);
printf("%s\n", buf);
fio_sock_close(fds[0]);
fio_sock_close(fds[1]);
return 0;
}
```
### TCP Server Sketch
```c
#define FIO_SOCK
#include "fio-stl.h"
int main(void) {
fio_socket_i s = fio_sock_open(NULL, "8080",
FIO_SOCK_SERVER | FIO_SOCK_TCP |
FIO_SOCK_NONBLOCK);
if (!FIO_SOCK_FD_ISVALID(s))
return 1;
if (FIO_SOCK_WAIT_R(s, 1000) & POLLIN) {
fio_socket_i c = fio_sock_accept(s, NULL, NULL);
if (FIO_SOCK_FD_ISVALID(c))
fio_sock_close(c);
}
fio_sock_close(s);
return 0;
}
```
---
# State Callbacks
```c
#define FIO_STATE
#include "fio-stl.h"
```
A small global event bus for lifecycle callbacks: initialize, fork, start, idle, shutdown, exit, and two user queues. Modules can listen without the caller knowing they exist. Sneaky, but polite. Implemented in [`./004 state callbacks.h`](https://facil.io/edge/api/004-state-callbacks/).
Use this as a global `FIO_EXTERN` module when multiple translation units should share the same callback registry.
### Types
#### `fio_state_event_type_e`
```c
typedef enum {
FIO_CALL_ON_INITIALIZE,
FIO_CALL_PRE_START,
FIO_CALL_BEFORE_FORK,
FIO_CALL_IN_CHILD,
FIO_CALL_IN_MASTER,
FIO_CALL_AFTER_FORK,
FIO_CALL_ON_WORKER_THREAD_START,
FIO_CALL_ON_START,
FIO_CALL_RESERVED1,
FIO_CALL_RESERVED2,
FIO_CALL_ON_USER1,
FIO_CALL_ON_USER2,
FIO_CALL_ON_IDLE,
FIO_CALL_ON_USER1_REVERSE,
FIO_CALL_ON_USER2_REVERSE,
FIO_CALL_RESERVED1_REVERSED,
FIO_CALL_RESERVED2_REVERSED,
FIO_CALL_ON_SHUTDOWN,
FIO_CALL_ON_PARENT_CRUSH,
FIO_CALL_ON_CHILD_CRUSH,
FIO_CALL_ON_WORKER_THREAD_END,
FIO_CALL_ON_STOP,
FIO_CALL_AT_EXIT,
FIO_CALL_AFTER_EXIT,
FIO_CALL_NEVER
} fio_state_event_type_e;
```
Event queues. `FIO_CALL_NEVER` is the sentinel and should not be used as an event.
**Ordering:**
- events `<= FIO_CALL_ON_IDLE` run in registration order
- events `>= FIO_CALL_ON_SHUTDOWN` run in reverse registration order
`FIO_CALL_ON_USER1` / `FIO_CALL_ON_USER2` and their reverse variants are available for user code.
### Callback Management
#### `fio_state_callback_add`
```c
SFUNC void fio_state_callback_add(fio_state_event_type_e,
void (*func)(void *),
void *arg);
```
Adds `func(arg)` to an event queue. Adding the same function / argument pair replaces the existing map entry rather than creating a duplicate.
If `FIO_CALL_ON_INITIALIZE` already ran, adding another initialize callback calls it immediately.
#### `fio_state_callback_remove`
```c
SFUNC int fio_state_callback_remove(fio_state_event_type_e,
void (*func)(void *),
void *arg);
```
Removes `func(arg)` from an event queue.
**Returns:** the internal map removal result, or `-1` if the event is invalid.
#### `fio_state_callback_clear`
```c
SFUNC void fio_state_callback_clear(fio_state_event_type_e e);
```
Clears all callbacks for `e`. Invalid events are ignored.
This function is implemented in the header's implementation section; with `FIO_EXTERN`, expose the implementation in exactly one translation unit as usual.
#### `fio_state_callback_force`
```c
SFUNC void fio_state_callback_force(fio_state_event_type_e);
```
Runs the callbacks for an event as if the event happened.
During a forced event, the callback list is copied first. Additions and removals for the same event do not affect the current run.
Special behavior:
- `FIO_CALL_ON_INITIALIZE` runs only once and reserves internal maps
- `FIO_CALL_IN_CHILD` reinitializes internal locks and reseeds randomness
- `FIO_CALL_AFTER_FORK` reseeds randomness
### Debug Helpers
#### `fio_state_callback_print_state`
```c
FIO_IFUNC void fio_state_callback_print_state(void);
```
Prints callback counts and capacities for every event queue to `stderr`.
### Thread-Safety
Each event queue has its own lock. Adding, removing, clearing, and forcing callbacks are synchronized while touching the queue. Callback execution happens after the queue has been copied and unlocked.
### Example
```c
#define FIO_STATE
#include "fio-stl.h"
#include
static void hello(void *arg) {
printf("hello %s\n", (const char *)arg);
}
int main(void) {
fio_state_callback_add(FIO_CALL_ON_USER1, hello, "callbacks");
fio_state_callback_force(FIO_CALL_ON_USER1);
fio_state_callback_remove(FIO_CALL_ON_USER1, hello, "callbacks");
return 0;
}
```
---
# Time Helpers
```c
#define FIO_TIME
#include "fio-stl.h"
```
Wall-clock and monotonic time helpers, UTC calendar conversion, and a handful of date formatters. Tiny clock toolbox, no sundial required. Implemented in [`./004 time.h`](https://facil.io/edge/api/004-time/).
### Collecting Time
#### `fio_time_real`
```c
FIO_IFUNC struct timespec fio_time_real(void);
```
Returns human / watch time using `CLOCK_REALTIME`. Good for timestamps, less good for measuring elapsed time because the system clock may jump.
#### `fio_time_mono`
```c
FIO_IFUNC struct timespec fio_time_mono(void);
```
Returns monotonic time using `CLOCK_MONOTONIC`. Prefer this for intervals and deadlines.
#### `fio_time_nano`
```c
FIO_IFUNC int64_t fio_time_nano(void);
```
Returns monotonic time in nanoseconds.
#### `fio_time_micro`
```c
FIO_IFUNC int64_t fio_time_micro(void);
```
Returns monotonic time in microseconds.
#### `fio_time_milli`
```c
FIO_IFUNC int64_t fio_time_milli(void);
```
Returns monotonic time in milliseconds.
### Conversion
#### `fio_time2milli`
```c
FIO_IFUNC int64_t fio_time2milli(struct timespec);
```
Converts a `struct timespec` to milliseconds.
#### `fio_time2micro`
```c
FIO_IFUNC int64_t fio_time2micro(struct timespec);
```
Converts a `struct timespec` to microseconds.
#### `fio_time2gm`
```c
SFUNC struct tm fio_time2gm(time_t time);
```
Converts `time` to a UTC `struct tm`. This is a faster, less localized alternative to `gmtime_r`.
#### `fio_gm2time`
```c
SFUNC time_t fio_gm2time(struct tm tm);
```
Converts a UTC `struct tm` to seconds. If `tm.tm_isdst > 0`, one hour is subtracted. On platforms with `tm_gmtoff`, that offset is added.
### Date Formatting
All formatting functions write to `target`, NUL-terminate the output, and return the number of bytes written before the NUL. Provide a buffer with enough space; `64` bytes is comfortably boring.
#### `fio_time2rfc7231`
```c
SFUNC size_t fio_time2rfc7231(char *target, time_t time);
```
Writes an RFC 7231 / HTTP date, usually 29 characters:
```text
Sun, 06 Nov 1994 08:49:37 GMT
```
#### `fio_time2rfc2109`
```c
SFUNC size_t fio_time2rfc2109(char *target, time_t time);
```
Writes an RFC 2109 cookie date, usually 31 characters:
```text
Sun, 06 Nov 1994 08:49:37 -0000
```
#### `fio_time2rfc2822`
```c
SFUNC size_t fio_time2rfc2822(char *target, time_t time);
```
Writes an RFC 2822 date, usually 28-29 characters:
```text
Sun, 6-Nov-1994 08:49:37 GMT
```
#### `fio_time2log`
```c
SFUNC size_t fio_time2log(char *target, time_t time);
```
Writes common log format:
```text
[DD/MMM/yyyy:hh:mm:ss +0000]
```
Usually needs 29 bytes including the NUL terminator.
#### `fio_time2iso`
```c
SFUNC size_t fio_time2iso(char *target, time_t time);
```
Writes an ISO-ish representation. The header comment says `YYYY-MM-DD HH:MM:SS`; the implementation writes a 3-letter month name:
```text
YYYY-MMM-DD HH:MM:SS
```
Usually needs 20 bytes including the NUL terminator.
### Arithmetic
#### `fio_time_add`
```c
FIO_IFUNC struct timespec fio_time_add(struct timespec t, struct timespec t2);
```
Adds two `struct timespec` values and normalizes the nanoseconds field.
#### `fio_time_add_milli`
```c
FIO_IFUNC struct timespec fio_time_add_milli(struct timespec t, int64_t milli);
```
Adds milliseconds to `t` and normalizes the result. The implementation splits milliseconds with a `1024`-millisecond approximation before normalization; cute, but be aware.
#### `fio_time_cmp`
```c
FIO_IFUNC int fio_time_cmp(struct timespec t1, struct timespec t2);
```
Compares two `struct timespec` values.
**Returns:** `-1` if `t1 < t2`, `0` if equal, `1` if `t1 > t2`.
### Example
```c
#define FIO_TIME
#include "fio-stl.h"
#include
#include
int main(void) {
struct timespec start = fio_time_mono();
struct timespec later = fio_time_add_milli(start, 500);
printf("now: %lld ms\n", (long long)fio_time_milli());
printf("later is after start: %d\n", fio_time_cmp(later, start) > 0);
char date[64];
fio_time2rfc7231(date, fio_time_real().tv_sec);
printf("http date: %s\n", date);
return 0;
}
```
---
# URL-Encoded Parser
```c
#define FIO_URL_ENCODED
#include "fio-stl.h"
```
A non-allocating parser for `application/x-www-form-urlencoded` data. It splits `name=value&...` pairs and leaves decoding to you. No copies, no drama. Implemented in [`./004 urlencoded.h`](https://facil.io/edge/api/004-urlencoded/).
### Types
#### `fio_url_encoded_parser_callbacks_s`
```c
typedef struct {
void *(*on_pair)(void *udata, fio_buf_info_s name, fio_buf_info_s value);
void (*on_error)(void *udata);
} fio_url_encoded_parser_callbacks_s;
```
Parser callbacks. `udata` is passed to callbacks and updated from `on_pair`'s return value.
**Members:**
- `on_pair` - called for each pair. `name` and `value` point into the original input and are **not decoded**. Return non-NULL to continue, or `NULL` to stop.
- `on_error` - optional and currently unused; reserved because parsers enjoy having a rainy-day pocket.
If `callbacks` or a member is `NULL`, an internal no-op callback is used.
#### `fio_url_encoded_result_s`
```c
typedef struct {
size_t consumed;
size_t count;
int err;
} fio_url_encoded_result_s;
```
Parse result.
**Members:**
- `consumed` - number of input bytes consumed
- `count` - number of non-empty pairs reported
- `err` - non-zero when parsing stopped because `on_pair` returned `NULL`
### Parsing
#### `fio_url_encoded_parse`
```c
SFUNC fio_url_encoded_result_s
fio_url_encoded_parse(const fio_url_encoded_parser_callbacks_s *callbacks,
void *udata,
const char *data,
size_t len);
```
Parses `data[0..len)` as URL-encoded form data.
Rules from the implementation:
- pairs are separated by `&`
- the first `=` in a pair separates name and value
- `name=` is valid and has an empty value
- `name` is valid and has an empty value
- `=value` is valid and has an empty name
- empty pairs such as leading `&` or `&&` are skipped
- percent escapes are not decoded
Use a string helper such as `fio_string_write_url_dec` when you need decoded values.
**Ownership:** callback buffers borrow the original `data`; keep `data` alive while parsing and copy anything you need later.
**Thread-safety:** parsing uses only caller-provided state and stack locals.
### Example
```c
#define FIO_URL_ENCODED
#include "fio-stl.h"
#include
#include
static void *on_pair(void *udata, fio_buf_info_s name, fio_buf_info_s value) {
printf("%.*s = %.*s\n",
(int)name.len, name.buf,
(int)value.len, value.buf);
return udata;
}
int main(void) {
const char form[] = "name=Bo&empty=&encoded=a%20b&&solo";
static const fio_url_encoded_parser_callbacks_s cb = {
.on_pair = on_pair,
};
fio_url_encoded_result_s r =
fio_url_encoded_parse(&cb, (void *)1, form, strlen(form));
printf("pairs=%zu consumed=%zu err=%d\n", r.count, r.consumed, r.err);
return r.err;
}
```
---
# WebSocket Parser
```c
#define FIO_WEBSOCKET_PARSER
#include FIO_INCLUDE_FILE
```
Small RFC 6455 frame parser and writer. It keeps 24 bytes of parser state,
allocates nothing, unmasks incoming payload bytes in-place, and reports one
parse event at a time.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the neighboring
[HTTP/1.x parser](./004 http1 parser.md), the higher-level
[HTTP module](./439 http.md), and WebSocket compression support in
[DEFLATE / Gzip](./162 deflate.md).
---
## What Gets Added
`FIO_WEBSOCKET_PARSER` exposes:
- `fio_websocket_s` — incremental parser state.
- `fio_websocket_event_s` — one parse event returned by
`fio_websocket_parse`.
- `fio_websocket_init` / `fio_websocket_reset` — parser lifecycle helpers.
- `fio_websocket_parse` — incremental parser entry point.
- frame writer helpers for server and client data, ping, pong, and close
frames.
- WebSocket close code, event type, opcode, RSV, parser state, flag, and error
constants.
The implementation also defines helpers named with `fio___websocket...`; those
are private implementation details.
---
## Parser State
### `fio_websocket_s`
```c
typedef struct fio_websocket_s {
uint64_t frame_remaining;
uint32_t mask;
uint32_t frame_consumed;
uint16_t close_code;
uint8_t state;
uint8_t flags;
uint8_t flags2;
uint8_t reserved;
} fio_websocket_s;
```
The parser state is intentionally small and is asserted to fit in 24 bytes.
Allocate it wherever the connection state lives, initialize it before use, and
prefer the public helper macros below instead of editing fields by hand.
```c
FIO_WEBSOCKET_GET_FIN(p)
FIO_WEBSOCKET_GET_MASKED(p)
FIO_WEBSOCKET_GET_OPCODE(p)
FIO_WEBSOCKET_GET_MSG_OPCODE(p)
FIO_WEBSOCKET_GET_PAUSED(p)
FIO_WEBSOCKET_GET_MSG_RSV(p)
```
`GET_OPCODE` reports the current frame opcode. `GET_MSG_OPCODE` reports the
open message opcode (`FIO_WEBSOCKET_OP_TEXT`, `FIO_WEBSOCKET_OP_BINARY`, or
`0` when no message is open). `GET_MSG_RSV` reports the RSV bits from the
opening data frame in the same 3-bit format used by the writer API.
### Lifecycle
```c
FIO_IFUNC void fio_websocket_init(fio_websocket_s *p);
FIO_IFUNC void fio_websocket_reset(fio_websocket_s *p);
```
Both helpers clear the struct and set the parser to the header-reading state.
After a protocol error or after receiving a close frame, discard or reset the
parser before reusing it.
---
## Events
### `fio_websocket_event_s`
```c
typedef struct {
uint8_t type;
uint8_t opcode;
uint8_t is_text;
uint8_t rsv;
uint8_t is_first;
uint8_t is_last;
fio_buf_info_s payload;
uint16_t close_code;
} fio_websocket_event_s;
```
`payload` points into the input buffer passed to `fio_websocket_parse`. If the
incoming frame is masked, that memory is unmasked in-place before the event is
returned. Keep the input bytes writable and alive until the event payload is no
longer needed.
### Event Types
```c
FIO_WEBSOCKET_EV_NONE
FIO_WEBSOCKET_EV_DATA_CHUNK
FIO_WEBSOCKET_EV_CONTROL
FIO_WEBSOCKET_EV_MESSAGE_END
FIO_WEBSOCKET_EV_ERROR
```
The current parser reports message data with `FIO_WEBSOCKET_EV_DATA_CHUNK` and
marks message boundaries with `is_first` and `is_last`. Control frames are
reported whole with `FIO_WEBSOCKET_EV_CONTROL`. Protocol errors are reported as
`FIO_WEBSOCKET_EV_ERROR` when an event pointer is supplied.
`FIO_WEBSOCKET_EV_MESSAGE_END` is defined for API completeness, but the current
parse loop uses the `is_last` flag on data events instead of emitting a separate
message-end event.
### Event Fields
For data events:
- `opcode` is the current frame opcode. Continuation frames report
`FIO_WEBSOCKET_OP_CONT`.
- `is_text` is true when the open message started as a text message.
- `rsv` is copied from the opening data frame.
- `is_first` is true for the first chunk of the opening data frame.
- `is_last` is true for the final chunk of the message.
- `payload` is the available payload chunk, possibly length `0`.
For control events:
- `opcode` is `FIO_WEBSOCKET_OP_CLOSE`, `FIO_WEBSOCKET_OP_PING`, or
`FIO_WEBSOCKET_OP_PONG`.
- `payload` is the complete control payload, never a partial control frame.
- close frames set `close_code` to the on-wire close code, or
`FIO_WEBSOCKET_CLOSE_NO_STATUS` for an empty close payload.
---
## Parsing API
```c
FIO_SFUNC size_t fio_websocket_parse(fio_websocket_s *p,
fio_buf_info_s buf,
fio_websocket_event_s *ev);
```
Parses WebSocket bytes from `buf` and returns the number of bytes consumed, or
`FIO_WEBSOCKET_PARSE_ERROR` (`(size_t)-1`) on protocol error.
A successful call may consume fewer bytes than supplied. Feed the remaining
bytes to the next call, usually in a loop. If more bytes are needed before an
event can be produced, the function returns the bytes consumed so far, which can
be `0`, and leaves `ev->type` as `FIO_WEBSOCKET_EV_NONE`.
The parser is pure state plus the caller-provided buffer:
- no heap allocation;
- no callbacks;
- no retained payload pointers;
- no internal message accumulator;
- no control-frame buffering beyond waiting until a complete control payload is
available in the supplied input.
`ev` may be `NULL` if the caller only wants to advance or validate input, but
then event details and close/error codes must be read from parser state where
available.
### Parser Flow
```text
header
├─ incomplete header -> wait for more bytes
├─ control frame -> wait for full payload -> CONTROL event
└─ data / continuation -> unmask available bytes -> DATA_CHUNK event
└─ is_last marks message completion
```
The parser returns after at most one event. For fragmented messages, each data
frame may produce one or more data chunks depending on the input buffer splits.
Control frames may appear between fragmented data frames and are reported as
control events without closing the open message.
### Protocol Checks Performed
The parser rejects:
- unknown opcodes;
- fragmented control frames;
- continuation frames without an open message;
- nested text/binary messages while another data message is open;
- control payloads larger than 125 bytes;
- 64-bit lengths with the high bit set;
- frames larger than `FIO_WEBSOCKET_DEFAULT_MAX_FRAME`;
- close frames with a 1-byte payload;
- close frames carrying invalid on-wire close codes.
On rejection, the parser state becomes `FIO_WEBSOCKET_STATE_ERROR`,
`p->close_code` is set, and `fio_websocket_parse` returns
`FIO_WEBSOCKET_PARSE_ERROR`.
Caller policy still includes:
- enforcing client/server masking rules;
- interpreting RSV bits and applying extension transforms such as
`permessage-deflate`;
- enforcing total message size limits;
- validating UTF-8 for text messages;
- accumulating message chunks if whole-message delivery is desired.
---
## Frame Writers
Server writers produce unmasked frames. Client writers produce masked frames;
passing `mask == 0` asks the writer to generate a non-zero PRNG mask.
### Sizing
```c
FIO_IFUNC uint64_t fio_websocket_write_len(uint64_t payload_len, _Bool masked);
```
Returns the number of bytes required for one complete frame with the requested
payload length and masking mode. Allocate at least this many bytes before
calling a writer.
### Data Messages
```c
FIO_IFUNC uint64_t fio_websocket_write_message_server(void *target,
fio_buf_info_s msg,
_Bool is_text,
uint8_t rsv);
FIO_IFUNC uint64_t fio_websocket_write_message_client(void *target,
fio_buf_info_s msg,
_Bool is_text,
uint32_t mask,
uint8_t rsv);
```
Writes one complete FIN data message to `target` and returns the bytes written.
`is_text` selects text (`1`) or binary (`0`). `rsv` is the 3-bit RSV value;
usually pass `0`, or `FIO_WEBSOCKET_RSV1` for a compressed
`permessage-deflate` message after applying the extension transform.
### Ping / Pong
```c
FIO_IFUNC uint64_t fio_websocket_write_ping_server(void *target,
fio_buf_info_s payload);
FIO_IFUNC uint64_t fio_websocket_write_ping_client(void *target,
fio_buf_info_s payload,
uint32_t mask);
FIO_IFUNC uint64_t fio_websocket_write_pong_server(void *target,
fio_buf_info_s payload);
FIO_IFUNC uint64_t fio_websocket_write_pong_client(void *target,
fio_buf_info_s payload,
uint32_t mask);
```
Writes one FIN control frame. Keep ping and pong payloads at 125 bytes or less;
the writer does not add a separate policy check for that RFC limit.
### Close
```c
FIO_IFUNC uint64_t fio_websocket_write_close_server(void *target,
uint16_t code,
fio_buf_info_s reason);
FIO_IFUNC uint64_t fio_websocket_write_close_client(void *target,
uint16_t code,
fio_buf_info_s reason,
uint32_t mask);
```
Writes a close frame containing the 2-byte close code followed by the optional
reason. The reason is truncated so the whole close payload fits in the 125-byte
control-frame limit. Choose a close code that is valid to send on the wire.
---
## Constants
### Opcodes
```c
FIO_WEBSOCKET_OP_CONT
FIO_WEBSOCKET_OP_TEXT
FIO_WEBSOCKET_OP_BINARY
FIO_WEBSOCKET_OP_CLOSE
FIO_WEBSOCKET_OP_PING
FIO_WEBSOCKET_OP_PONG
```
### Close Codes
```c
FIO_WEBSOCKET_CLOSE_OK
FIO_WEBSOCKET_CLOSE_GOING_AWAY
FIO_WEBSOCKET_CLOSE_PROTOCOL_ERROR
FIO_WEBSOCKET_CLOSE_UNSUPPORTED_DATA
FIO_WEBSOCKET_CLOSE_NO_STATUS
FIO_WEBSOCKET_CLOSE_INVALID_PAYLOAD
FIO_WEBSOCKET_CLOSE_POLICY_VIOLATION
FIO_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG
FIO_WEBSOCKET_CLOSE_MANDATORY_EXT
FIO_WEBSOCKET_CLOSE_INTERNAL_ERROR
```
`FIO_WEBSOCKET_CLOSE_NO_STATUS` is synthesized for an empty received close
payload and must not be sent as an on-wire status code. The parser also accepts
valid registered wire codes such as 1012-1014 and application/library codes in
the 3000-4999 range.
### RSV Bits
```c
FIO_WEBSOCKET_RSV1
FIO_WEBSOCKET_RSV2
FIO_WEBSOCKET_RSV3
```
These are 3-bit values for the writer API and event `rsv` field. The writer
shifts them into byte-0 bits 4..6 on the wire.
### Parser States and Limits
```c
FIO_WEBSOCKET_STATE_HEADER
FIO_WEBSOCKET_STATE_PAYLOAD
FIO_WEBSOCKET_STATE_CLOSED
FIO_WEBSOCKET_STATE_ERROR
FIO_WEBSOCKET_DEFAULT_MAX_FRAME
FIO_WEBSOCKET_PARSE_ERROR
```
`FIO_WEBSOCKET_DEFAULT_MAX_FRAME` defaults to 1 GiB and may be overridden before
including the header. `FIO_WEBSOCKET_PARSE_ERROR` is the parse error sentinel.
The header also exposes flag bit masks used by the accessor macros:
`FIO_WEBSOCKET_FLAG_FIN`, `FIO_WEBSOCKET_FLAG_MASKED`,
`FIO_WEBSOCKET_FLAG_OPCODE_MASK`, `FIO_WEBSOCKET_FLAG_OPCODE_SHIFT`,
`FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK`, `FIO_WEBSOCKET_FLAG2_PAUSED`,
`FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK`, and
`FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT`.
---
## Minimal Parse Loop
```c
#define FIO_WEBSOCKET_PARSER
#include FIO_INCLUDE_FILE
typedef struct {
fio_websocket_s ws;
} connection_s;
void websocket_consume(connection_s *c, char *data, size_t len) {
fio_buf_info_s buf = FIO_BUF_INFO2(data, len);
while (buf.len) {
fio_websocket_event_s ev;
size_t n = fio_websocket_parse(&c->ws, buf, &ev);
if (n == FIO_WEBSOCKET_PARSE_ERROR) {
/* Send/record c->ws.close_code and close the connection. */
return;
}
buf.buf += n;
buf.len -= n;
if (!n && ev.type == FIO_WEBSOCKET_EV_NONE)
break; /* wait for more network bytes */
switch (ev.type) {
case FIO_WEBSOCKET_EV_DATA_CHUNK:
/* ev.payload is already unmasked and points into data. */
if (ev.is_first) {
/* start a message accumulator, if needed */
}
if (ev.payload.len) {
/* copy/process this chunk before reusing data */
}
if (ev.is_last) {
/* finish the message */
}
break;
case FIO_WEBSOCKET_EV_CONTROL:
if (ev.opcode == FIO_WEBSOCKET_OP_PING) {
char out[128 + 14];
uint64_t written = fio_websocket_write_pong_server(out, ev.payload);
(void)written; /* write out to the socket */
} else if (ev.opcode == FIO_WEBSOCKET_OP_CLOSE) {
/* Echo/close according to local policy. */
return;
}
break;
default:
break;
}
}
}
void websocket_open(connection_s *c) {
fio_websocket_init(&c->ws);
}
```
For real IO, preserve any unconsumed bytes and retry when more data arrives.
Copy payload bytes before recycling the read buffer or applying asynchronous
message handling.
---
## Minimal Write Example
```c
char out[14 + 1024];
fio_buf_info_s msg = FIO_BUF_INFO2("hello", 5);
uint64_t len = fio_websocket_write_message_server(out, msg, 1, 0);
/* write `len` bytes from out */
```
For client frames:
```c
uint64_t len = fio_websocket_write_message_client(out, msg, 1, 0, 0);
```
The fourth argument is an explicit mask; `0` lets the writer choose one.
---
## Ownership and Lifetime
- The parser owns only `fio_websocket_s` state supplied by the caller.
- The parser never allocates, frees, stores callbacks, or retains payload
pointers after returning.
- Input buffers passed to `fio_websocket_parse` must be writable because masked
payloads are modified in-place.
- Event payload slices are valid only while the input buffer remains valid and
unchanged.
- Writer targets must be large enough for `fio_websocket_write_len(payload.len,
masked)` bytes.
- Independent parser instances can be used concurrently by different threads;
synchronize shared buffers and connection state in the caller.
# CLI Helpers
```c
#define FIO_CLI
#include "fio-stl.h"
```
A small command-line parser with auto-generated help, typed flags, and unnamed-argument support. It is easier to set up than `getopt` and stays out of the way once you have your values.
Defining `FIO_CLI` also pulls in `FIO_ATOL`, `FIO_RAND`, and `FIO_IMAP` because the parser needs them internally.
**Note:** the module keeps a global data table. Parsing and setting values is **not** thread-safe. Reading values is safe after parsing is complete.
---
## Types
#### `fio_cli_arg_e`
```c
typedef enum {
FIO_CLI_ARG_STRING,
FIO_CLI_ARG_BOOL,
FIO_CLI_ARG_INT,
FIO_CLI_ARG_PRINT,
FIO_CLI_ARG_PRINT_LINE,
FIO_CLI_ARG_PRINT_HEADER,
} fio_cli_arg_e;
```
Argument type tags used internally and returned by `fio_cli_type`.
**Values:**
- `FIO_CLI_ARG_STRING` — accepts any string value.
- `FIO_CLI_ARG_BOOL` — a flag; presence means true.
- `FIO_CLI_ARG_INT` — validates the value as an integer.
- `FIO_CLI_ARG_PRINT` — indented help text line.
- `FIO_CLI_ARG_PRINT_LINE` — unindented help text line.
- `FIO_CLI_ARG_PRINT_HEADER` — section header in help output.
---
## Argument Definition Macros
Use these inside the `fio_cli_start` macro to describe each flag and help line.
#### `FIO_CLI_STRING`
```c
#define FIO_CLI_STRING(line) /* ... */
```
Declares a string argument.
```c
FIO_CLI_STRING("-o -output (stdout) output file path")
```
#### `FIO_CLI_INT`
```c
#define FIO_CLI_INT(line) /* ... */
```
Declares an integer argument. The parser validates the value with `fio_atol`.
```c
FIO_CLI_INT("-p -port (8080) the port number to use")
```
#### `FIO_CLI_BOOL`
```c
#define FIO_CLI_BOOL(line) /* ... */
```
Declares a boolean flag. Boolean flags cannot have default values. They can be chained, e.g. `-abc` is treated as `-a -b -c`.
```c
FIO_CLI_BOOL("-v -verbose enable verbose logging")
```
#### `FIO_CLI_PRINT`
```c
#define FIO_CLI_PRINT(line) /* ... */
```
Prints an extra indented help line after the previous argument.
```c
FIO_CLI_INT("-p -port (8080) the port number"),
FIO_CLI_PRINT("Set to 0 for Unix socket mode.")
```
#### `FIO_CLI_PRINT_LINE`
```c
#define FIO_CLI_PRINT_LINE(line) /* ... */
```
Prints a help line without indentation.
#### `FIO_CLI_PRINT_HEADER`
```c
#define FIO_CLI_PRINT_HEADER(line) /* ... */
```
Prints a section header in the help output.
```c
FIO_CLI_PRINT_HEADER("Network Options:")
```
---
## Initialization and Cleanup
#### `fio_cli_start`
```c
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
fio_cli_start((argc), \
(argv), \
(unnamed_min), \
(unnamed_max), \
(description), \
(fio___cli_line_s[]){__VA_ARGS__, {0}})
SFUNC void fio_cli_start FIO_NOOP(int argc,
char const *argv[],
int unnamed_min,
int unnamed_max,
char const *description,
fio___cli_line_s *arguments);
```
Parses `argv` into a dictionary of named and unnamed arguments, then prints help and exits on `-h`, `-?`, `-help`, or `--help`.
**Named Arguments:**
- `argc`, `argv` — values passed to `main`.
- `unnamed_min` — minimum required unnamed arguments. `-1` means no limit on unnamed arguments.
- `unnamed_max` — maximum allowed unnamed arguments. `-1` means unlimited.
- `description` — program description shown in help. The text `NAME` is replaced with `argv[0]`.
- `...` — argument description lines using the macros above.
Argument names must start with `-`. The first non-`-` word begins the description. Optional defaults go in parentheses: `(default)` or `("literal default")`.
Accepted input formats:
```text
app -t=1 -p3000 -a localhost
app -t 1 -p 3000 -a localhost
app --threads=1 --port=3000 --address=localhost
```
**Note:** this function is **not** thread-safe.
```c
#define FIO_CLI
#include "fio-stl.h"
int main(int argc, char const *argv[]) {
fio_cli_start(argc, argv, 0, -1,
"NAME - a small CLI example.\n",
FIO_CLI_PRINT_HEADER("Options:"),
FIO_CLI_STRING("-s -str (hello) a string"),
FIO_CLI_INT("-i -int (42) an integer"),
FIO_CLI_BOOL("-b -bool a boolean flag"),
FIO_CLI_PRINT("Boolean flags do not take values."));
fprintf(stderr, "string: %s\n", fio_cli_get("-s"));
fprintf(stderr, "int: %" PRId64 "\n", fio_cli_get_i("-i"));
fprintf(stderr, "bool: %d\n", (int)fio_cli_get_bool("-b"));
fio_cli_end();
return 0;
}
```
#### `fio_cli_end`
```c
SFUNC void fio_cli_end(void);
```
Frees the parsed CLI dictionary.
A destructor runs this automatically at program exit, but calling it earlier lets the memory be reused.
**Note:** this function is **not** thread-safe.
---
## Getting Values
#### `fio_cli_get`
```c
SFUNC char const *fio_cli_get(char const *name);
```
Returns the argument value as a NUL-terminated C string, or `NULL` if the argument was not provided. If `name` is `NULL`, returns the first unnamed argument.
#### `fio_cli_get_str`
```c
SFUNC fio_buf_info_s fio_cli_get_str(char const *name);
```
Returns the argument value as a `fio_buf_info_s` with length. If `name` is `NULL`, returns the first unnamed argument.
#### `fio_cli_get_i`
```c
SFUNC int64_t fio_cli_get_i(char const *name);
```
Returns the argument value parsed as an integer, or `0` if it was not provided. Integer parsing accepts decimal, hex, octal, and binary prefixes.
#### `fio_cli_get_bool`
```c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
```
Returns non-zero if the argument was provided, including by default.
#### `fio_cli_type`
```c
SFUNC fio_cli_arg_e fio_cli_type(char const *name);
```
Returns the declared argument type, or `FIO_CLI_ARG_NONE` if the name is unknown.
---
## Unnamed Arguments
#### `fio_cli_unnamed_count`
```c
SFUNC unsigned int fio_cli_unnamed_count(void);
```
Returns the number of unnamed arguments collected.
#### `fio_cli_unnamed`
```c
SFUNC char const *fio_cli_unnamed(unsigned int index);
```
Returns the unnamed argument at `index` as a NUL-terminated string, or `NULL` if `index` is out of bounds.
#### `fio_cli_unnamed_str`
```c
SFUNC fio_buf_info_s fio_cli_unnamed_str(unsigned int index);
```
Returns the unnamed argument at `index` as a `fio_buf_info_s`, or an empty `fio_buf_info_s` if out of bounds.
---
## Setting Values
#### `fio_cli_set`
```c
SFUNC void fio_cli_set(char const *name, char const *value);
```
Sets or overwrites a named argument's value. If `name` is `NULL`, appends `value` as a new unnamed argument.
**Note:** this function is **not** thread-safe.
#### `fio_cli_set_i`
```c
SFUNC void fio_cli_set_i(char const *name, int64_t i);
```
Sets a named argument to a base-10 string representation of `i`.
**Note:** this function is **not** thread-safe.
#### `fio_cli_set_unnamed`
```c
SFUNC unsigned int fio_cli_set_unnamed(unsigned int index, const char *value);
```
Sets or appends an unnamed argument. If `index` is past the end, the value is appended. Returns the stored index, or `(unsigned int)-1` if `value` is `NULL` or empty.
**Note:** this function is **not** thread-safe.
---
## Iteration
#### `fio_cli_each`
```c
SFUNC size_t fio_cli_each(int (*task)(fio_buf_info_s name,
fio_buf_info_s value,
fio_cli_arg_e arg_type,
void *udata),
void *udata);
```
Calls `task` for every argument that has a value, returning the number of calls made. If `task` returns non-zero, iteration stops. For unnamed arguments, `name.buf` is `NULL` and `name.len` is `0`.
------------------------------------------------------------
# Memory Allocator
```c
#define FIO_MEMORY_NAME fio
#include "fio-stl.h"
```
A concurrent, per-arena memory allocator that groups objects with similar lifetimes. It tries to keep related allocations in the same chunk so cleanup is cheap and locality is decent. Define `FIO_MEMORY_DISABLE` to route everything to the system `malloc`/`free` instead.
The prefix `fio` comes from `FIO_MEMORY_NAME`. All public function names are built with `FIO_NAME(FIO_MEMORY_NAME, ...)`, so a custom prefix produces `my_malloc`, `my_free`, and so on.
The module also sets the temporary allocator macros (`FIO_MEM_REALLOC_`, `FIO_MEM_FREE_`, `FIO_MEM_REALLOC_IS_SAFE_`, `FIO_MEM_ALIGNMENT_SIZE_`) for any facil.io type defined in the same `include` scope.
**Note:** `FIO_MALLOC` is a shortcut that defines this allocator with prefix `fio` and general-purpose tuning.
---
## Core API
#### `fio_malloc`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, malloc)(size_t size);
```
Allocates `size` bytes aligned to `FIO_MEMORY_ALIGN_SIZE`. Memory is zeroed if `FIO_MEMORY_INITIALIZE_ALLOCATIONS` is enabled. Allocations above the arena limit use big-block mode or `mmap`.
**Parameters:**
- `size` — bytes to allocate.
**Returns:** pointer to allocated memory, or `NULL` on failure.
#### `fio_calloc`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc)(size_t size_per_unit,
size_t unit_count);
```
Equivalent to `fio_malloc(size_per_unit * unit_count)`.
#### `fio_realloc`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc)(void *ptr,
size_t new_size);
```
Reallocates `ptr` to `new_size`. Copy-free expansion is only attempted for large allocations. Prefer `fio_realloc2` for predictable zeroing.
**Parameters:**
- `ptr` — existing allocation, or `NULL`.
- `new_size` — requested new size.
**Returns:** pointer to the new block, or `NULL` on failure.
#### `fio_realloc2`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc2)(void *ptr,
size_t new_size,
size_t copy_len);
```
Reallocates `ptr` to `new_size`, copying at most `copy_len` bytes. Memory beyond `copy_len` is zeroed when `FIO_MEMORY_INITIALIZE_ALLOCATIONS` is enabled.
**Parameters:**
- `ptr` — existing allocation, or `NULL`.
- `new_size` — requested new size.
- `copy_len` — maximum bytes to copy from the old block.
**Returns:** pointer to the new block, or `NULL` on failure.
#### `fio_free`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, free)(void *ptr);
```
Frees memory allocated by this allocator. Freeing a pointer from a different allocator, or after cleanup, is undefined behavior.
#### `fio_mmap`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, mmap)(size_t size);
```
Allocates directly from the system with `mmap` semantics. Use this for large, long-lived objects. The allocation is slower but avoids arena bookkeeping.
**Parameters:**
- `size` — bytes to allocate.
**Returns:** pointer to allocated memory, or `NULL` on failure.
#### `fio_malloc_after_fork`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_after_fork)(void);
```
Reinitializes allocator locks after `fork` in the child process. Best effort only: forking a multi-threaded process is unsafe. Prefer calling `fio_state_callback_force(FIO_CALL_IN_CHILD);` to reset all registered allocators.
#### `fio_realloc_is_safe`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, realloc_is_safe)(void);
```
Returns non-zero if the allocator zeroes allocations, which makes `fio_realloc2` safe for the uncopied tail.
---
## Aligned Allocation API
The aligned API requests a minimum pointer alignment. `fio_malloc_aligned` and `fio_calloc_aligned` are thin wrappers around `fio_realloc_aligned`, which is the single entry point.
#### `fio_realloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME,
realloc_aligned)(void *ptr,
size_t new_size,
size_t copy_len,
size_t alignment);
```
Reallocates `ptr` to `new_size` (copying at most `copy_len` bytes, like `fio_realloc2`), guaranteeing the returned pointer is aligned to at least `alignment`.
The `alignment` argument is normalized:
- `0` selects the allocator default (`FIO_MEMORY_ALIGN_SIZE`).
- Non power-of-2 values are rounded **down** to the nearest power of 2.
- The effective alignment is the maximum of the (rounded) request, the current alignment of `ptr`, and `FIO_MEMORY_ALIGN_SIZE` — alignment never regresses across a realloc.
- Values above `FIO_MEMORY_SYS_ALLOCATION_SIZE` fail: returns `NULL` and sets `errno` to `EINVAL` (debug builds also trip `FIO_ASSERT_DEBUG`).
In-place growth only occurs when the existing pointer already satisfies the requested alignment; otherwise the data is copied to a newly aligned block.
`realloc_aligned(NULL, size, 0, alignment)` allocates (like `malloc_aligned`), and `realloc_aligned(ptr, 0, copy_len, alignment)` frees `ptr`.
#### `fio_malloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
malloc_aligned)(size_t size,
size_t alignment);
```
Equivalent to `fio_realloc_aligned(NULL, size, 0, alignment)`. A zero `size` returns the shared zero-allocation pointer (aligned only to `FIO_MEMORY_ALIGN_SIZE`), which may be freed or realloc'd normally.
#### `fio_calloc_aligned`
```c
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc_aligned)(size_t size_per_unit,
size_t unit_count,
size_t alignment);
```
Same as `fio_malloc_aligned(size_per_unit * unit_count, alignment)`, but zeroes the (rounded) allocation. Returns `NULL` on size overflow.
#### `fio_alloc_size`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, alloc_size)(size_t minimum_bytes);
```
Returns the number of usable bytes the allocator actually reserves for a request of `minimum_bytes` (the size class): rounded up to `FIO_MEMORY_ALIGN_SIZE` for arena/big-block allocations, and to whole pages (minus the header offset) for page-backed ones. Alignment padding is reserved in addition to the returned value. With `FIO_MEMORY_DISABLE` the system allocator exposes no rounding guarantee and `minimum_bytes` is returned unchanged.
### Alignment abstraction macros
```c
FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment)
FIO_MEM_FREE_ALIGNED(ptr, size)
FIO_MEM_ALLOC_SIZE(size)
```
`FIO_MEM_REALLOC_ALIGNED` mirrors `FIO_MEM_REALLOC` with an added `alignment` argument and **assigns** the result to `ptr` (test with `FIO_ASSERT_ALLOC(ptr)` after the call, restoring a saved copy on failure). `FIO_MEM_FREE_ALIGNED` releases memory obtained through the aligned API, and `FIO_MEM_ALLOC_SIZE` mirrors `fio_alloc_size`.
The macros route to the active allocator, exactly like `FIO_MEM_REALLOC` does: the fio allocator when it was included (`H___FIO_MALLOC___H`), or portable system-allocator fallbacks otherwise. Template-local variants (`FIO_MEM_REALLOC_ALIGNED_`, `FIO_MEM_FREE_ALIGNED_`) exist for `FIO_MEMORY_NAME` template scope, mirroring `FIO_MEM_REALLOC_` / `FIO_MEM_FREE_`.
**Pairing rule:** with the fio allocator, `free` accepts any pointer returned by the aligned API (interior pointers included), so `FIO_MEM_FREE_ALIGNED` is identical to `FIO_MEM_FREE`. Under `FIO_MEMORY_DISABLE` on Windows the aligned API uses `_aligned_malloc` / `_aligned_realloc`, whose memory **must** be released with `FIO_MEM_FREE_ALIGNED` (`_aligned_free`) — plain `free` is heap corruption. On POSIX both map to `free`. Custom allocators that stash alignment bookkeeping near the returned pointer should hook the macro pair, not the fio functions.
### Alignment guarantees per configuration
| Configuration | Default alignment | Aligned API backend | Max alignment |
|---|---|---|---|
| fio allocator (any OS) | `FIO_MEMORY_ALIGN_SIZE` (64) | interior-pointer slice / big-block / page-backed | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, POSIX | `_Alignof(max_align_t)` | `posix_memalign` + `free` | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, Windows | `_Alignof(max_align_t)` (16) | `_aligned_malloc` / `_aligned_realloc` / `_aligned_free` | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
| `FIO_MEMORY_DISABLE`, other | `_Alignof(max_align_t)` | over-allocation with stashed raw pointer | `FIO_MEMORY_SYS_ALLOCATION_SIZE` |
When `FIO_MALLOC_OVERRIDE_SYSTEM` is defined, the libc aligned-allocation family is overridden as well (`aligned_alloc`, `posix_memalign`, and on Windows `_aligned_malloc` / `_aligned_realloc` / `_aligned_free`), so libc-aligned pointers never reach the fio allocator (and vice versa).
---
## Configuration Macros
#### `FIO_MEMORY_NAME`
```c
#define FIO_MEMORY_NAME fio
```
Required prefix for the allocator's public symbols. Must be defined before including the header.
#### `FIO_MALLOC`
```c
#define FIO_MALLOC
```
Shortcut that defines the allocator with prefix `fio` and tunes it for general use. It also redefines `FIO_MEM_REALLOC`, `FIO_MEM_FREE`, `FIO_MEM_REALLOC_IS_SAFE`, and `FIO_MEM_ALIGNMENT_SIZE`.
#### `FIO_MEMORY_DISABLE`
```c
#define FIO_MEMORY_DISABLE
```
Bypasses the custom allocator and routes all allocations to the system `malloc`/`free`.
#### `FIO_MEMORY_ALIGN_LOG`
```c
#define FIO_MEMORY_ALIGN_LOG 6
```
Log2 of the allocation alignment. Clamped to the range `3`–`10` (8 to 1024 bytes). Default is `6` (64 bytes).
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG 21
```
Log2 of the chunk size allocated from the system. Clamped to `17`–`24`. Default is `21` (~2 MB).
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG 2
```
Log2 of the number of blocks per system chunk. Range `0`–`4`. Affects fragmentation and the threshold for big allocations.
#### `FIO_MEMORY_ENABLE_BIG_ALLOC`
```c
#define FIO_MEMORY_ENABLE_BIG_ALLOC 1
```
When enabled, large allocations can consume an entire chunk as a single big block. May increase fragmentation for long-lived objects.
#### `FIO_MEMORY_ARENA_COUNT`
```c
#define FIO_MEMORY_ARENA_COUNT -1
```
Number of arenas. Negative or zero means dynamic selection based on CPU core count, capped by `FIO_MEMORY_ARENA_COUNT_MAX` and fallback `FIO_MEMORY_ARENA_COUNT_FALLBACK`.
#### `FIO_MEMORY_ARENA_COUNT_FALLBACK`
```c
#define FIO_MEMORY_ARENA_COUNT_FALLBACK 24
```
Default arena count when dynamic CPU detection fails.
#### `FIO_MEMORY_ARENA_COUNT_MAX`
```c
#define FIO_MEMORY_ARENA_COUNT_MAX 64
```
Upper bound for dynamic arena count.
#### `FIO_MEMORY_USE_THREAD_MUTEX`
```c
#define FIO_MEMORY_USE_THREAD_MUTEX 0
```
When `1`, arenas protect themselves with `pthread` mutexes instead of spinlocks. Default is `1` when `FIO_MEMORY_ARENA_COUNT` is positive.
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS \
FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT
```
When `1`, allocations return zeroed memory and `realloc2` zeroes uncopied bytes. Default is `1`.
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT 1
```
Default value for `FIO_MEMORY_INITIALIZE_ALLOCATIONS`.
#### `FIO_MEMORY_CACHE_SLOTS`
```c
#define FIO_MEMORY_CACHE_SLOTS 4
```
Number of freed chunks to keep cached before returning memory to the system.
#### `FIO_MEMORY_WARMUP`
```c
#define FIO_MEMORY_WARMUP 0
```
If set to a positive number, that many arenas pre-allocate a block at startup. Usually best left at `0`.
#### `FIO_MEM_SYS_ALLOC`, `FIO_MEM_SYS_REALLOC`, `FIO_MEM_SYS_FREE`
```c
#define FIO_MEM_SYS_ALLOC(pages, alignment_log) /* ... */
#define FIO_MEM_SYS_REALLOC(ptr, old_pages, new_pages, alignment_log) /* ... */
#define FIO_MEM_SYS_FREE(ptr, pages) /* ... */
```
Override all three to replace the system allocation backend. Alignment is essential and may be large.
---
## Introspection
These functions are exposed for debugging. Treat them as unstable.
#### `fio_malloc_arenas`
```c
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arenas)(void);
```
Returns the number of arenas.
#### `fio_malloc_block_size`
```c
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_block_size)(void);
```
Returns the block size used for arena allocations.
#### `fio_malloc_sys_alloc_size`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_sys_alloc_size)(void);
```
Returns the system chunk size in bytes.
#### `fio_malloc_cache_slots`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_cache_slots)(void);
```
Returns the configured number of cache slots.
#### `fio_malloc_alignment`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment)(void);
```
Returns the allocation alignment in bytes.
#### `fio_malloc_alignment_log`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment_log)(void);
```
Returns log2 of the allocation alignment.
#### `fio_malloc_alloc_limit`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alloc_limit)(void);
```
Returns the size above which allocations bypass arena blocks.
#### `fio_malloc_arena_alloc_limit`
```c
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arena_alloc_limit)(void);
```
Returns the arena block size limit.
#### `fio_malloc_print_state`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_state)(void);
```
Prints allocator state to `stderr`.
#### `fio_malloc_print_free_block_list`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_free_block_list)(void);
```
Prints the free-block list to `stderr`.
#### `fio_malloc_print_settings`
```c
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_settings)(void);
```
Prints allocator settings to `stderr`.
---
## Example
```c
#define FIO_MALLOC
#include "fio-stl.h"
int main(void) {
char *buf = fio_malloc(256);
if (!buf) return -1;
fio_memcpy(buf, "hello", 5);
fprintf(stderr, "%s\n", buf);
char *buf2 = fio_realloc2(buf, 512, 5);
if (!buf2) {
fio_free(buf);
return -1;
}
buf = buf2;
fio_free(buf);
return 0;
}
```
------------------------------------------------------------
# String Core
```c
#define FIO_STR
#include "fio-stl.h"
```
Binary-safe string helpers for building, editing, escaping, encoding, and comparing text. This module works with the lightweight `fio_str_info_s` and `fio_buf_info_s` descriptors, plus two owned string types: copy-on-write `fio_bstr` and hash-map key string `fio_keystr_s`.
All authoring helpers take a `fio_string_realloc_fn` callback so they can grow either a stack buffer or a heap allocation.
---
## Types
#### `fio_string_realloc_fn`
```c
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len);
```
Callback that grows `dest->buf` to at least `len + 1` bytes and updates `dest->capa`. Returns `0` on success, `-1` on failure.
#### `fio_string_write_s`
```c
typedef struct {
size_t klass;
union {
struct { size_t len; const char *buf; } str;
double f;
int64_t i;
uint64_t u;
} info;
} fio_string_write_s;
```
Argument item used by `fio_string_write2` and `fio_bstr_write2`. The `klass` field selects the union member: `1` = string, `2` = signed int, `3` = unsigned int, `4` = hex, `5` = binary, `6` = double.
---
## Authorship Helpers
#### `fio_string_write`
```c
SFUNC int fio_string_write(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *restrict src,
size_t len);
```
Appends `len` bytes from `src` to `dest`. Grows `dest` if `reallocate` is provided; otherwise truncates when full. Always NUL-terminates on success.
**Parameters:**
- `dest` — target string descriptor. Must have a valid buffer and `capa >= len`.
- `reallocate` — growth callback, may be `NULL`.
- `src` — data to append.
- `len` — bytes to append.
**Returns:** `0` on success, `-1` if reallocation was needed and failed.
#### `fio_string_replace`
```c
SFUNC int fio_string_replace(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
intptr_t start_pos,
size_t overwrite_len,
const void *src,
size_t len);
```
Inserts, overwrites, or deletes a slice. Negative `start_pos` counts from the end (`-1` = last byte). When `overwrite_len` is `0`, the data is inserted. When `len` is `0`, the slice is deleted.
**Parameters:**
- `start_pos` — byte offset where editing begins.
- `overwrite_len` — bytes to overwrite or remove.
- `src` — replacement data, ignored if `len == 0`.
- `len` — replacement length.
**Returns:** `0` on success, `-1` on failure.
#### `fio_string_write2`
```c
SFUNC int fio_string_write2(fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate,
const fio_string_write_s srcs[]);
#define fio_string_write2(dest, reallocate, ...) \
fio_string_write2((dest), \
(reallocate), \
(fio_string_write_s[]){__VA_ARGS__, {0}})
```
Appends a mixed list of strings and numbers in a single pass. The macro builds a temporary `fio_string_write_s[]` array and appends a zero terminator.
```c
fio_string_write2(&str, my_realloc,
FIO_STRING_WRITE_STR1("The answer is: "),
FIO_STRING_WRITE_NUM(42),
FIO_STRING_WRITE_STR2(" (0x", 4),
FIO_STRING_WRITE_HEX(42),
FIO_STRING_WRITE_STR2(")", 1));
```
#### `FIO_STRING_WRITE_*`
```c
#define FIO_STRING_WRITE_STR1(str_) /* string with FIO_STRLEN length */
#define FIO_STRING_WRITE_STR2(str_, len_) /* string with explicit length */
#define FIO_STRING_WRITE_STR_INFO(str_) /* from fio_str_info_s */
#define FIO_STRING_WRITE_NUM(num) /* signed integer */
#define FIO_STRING_WRITE_UNUM(num) /* unsigned integer */
#define FIO_STRING_WRITE_HEX(num) /* hex representation */
#define FIO_STRING_WRITE_BIN(num) /* binary representation */
#define FIO_STRING_WRITE_FLOAT(num) /* double */
```
Helper macros that build `fio_string_write_s` items for `fio_string_write2`.
---
## Numeral Helpers
#### `fio_string_write_i`
```c
SFUNC int fio_string_write_i(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int64_t i);
```
Appends a signed base-10 integer.
#### `fio_string_write_u`
```c
SFUNC int fio_string_write_u(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends an unsigned base-10 integer.
#### `fio_string_write_hex`
```c
SFUNC int fio_string_write_hex(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends a lowercase hex representation.
#### `fio_string_write_bin`
```c
SFUNC int fio_string_write_bin(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
uint64_t i);
```
Appends a binary representation.
---
## printf Support
#### `fio_string_printf`
```c
SFUNC FIO___PRINTF_STYLE(3, 0) int fio_string_printf(
fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *format,
...);
```
Appends formatted output, growing the buffer as needed.
#### `fio_string_vprintf`
```c
SFUNC FIO___PRINTF_STYLE(3, 0) int fio_string_vprintf(
fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *format,
va_list argv);
```
`va_list` variant of `fio_string_printf`.
---
## Escaping and Encoding
#### `fio_string_write_escape`
```c
SFUNC int fio_string_write_escape(fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
```
Escapes `raw` using JSON string rules and appends the result.
#### `fio_string_write_unescape`
```c
SFUNC int fio_string_write_unescape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *enscaped,
size_t enscaped_len);
```
Unescapes JSON-style data and appends it.
#### `fio_string_write_base32enc` / `fio_string_write_base32dec`
```c
SFUNC int fio_string_write_base32enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_base32dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
Base32 encode / decode.
#### `fio_string_write_base64enc` / `fio_string_write_base64dec`
```c
SFUNC int fio_string_write_base64enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len,
uint8_t url_encoded);
SFUNC int fio_string_write_base64dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
Base64 encode / decode. Pass `url_encoded = 1` to use URL-safe alphabet.
#### `fio_string_write_url_enc` / `fio_string_write_url_dec` / `fio_string_write_path_dec`
```c
SFUNC int fio_string_write_url_enc(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_url_dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
SFUNC int fio_string_write_path_dec(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *encoded,
size_t encoded_len);
```
URL (percent) encoding and decoding. `url_dec` converts `+` to space; `path_dec` does not.
#### `fio_string_write_html_escape` / `fio_string_write_html_unescape`
```c
SFUNC int fio_string_write_html_escape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *raw,
size_t raw_len);
SFUNC int fio_string_write_html_unescape(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const void *enscaped,
size_t enscaped_len);
```
HTML escape / unescape. The unescape implementation is minimal and incomplete.
---
## File Reading
#### `fio_string_readfd`
```c
SFUNC int fio_string_readfd(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int fd,
intptr_t start_at,
size_t limit);
```
Reads up to `limit` bytes from seekable file descriptor `fd` starting at `start_at`. Negative `start_at` counts from EOF. `limit == 0` reads to EOF.
#### `fio_string_readfile`
```c
SFUNC int fio_string_readfile(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *filename,
intptr_t start_at,
size_t limit);
```
Opens `filename` and reads its contents (or a slice) into `dest`.
#### `fio_string_getdelim_fd`
```c
SFUNC int fio_string_getdelim_fd(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
int fd,
intptr_t start_at,
char delim,
size_t limit);
```
Reads from `fd` until `delim` or EOF.
#### `fio_string_getdelim_file`
```c
SFUNC int fio_string_getdelim_file(fio_str_info_s *dest,
fio_string_realloc_fn reallocate,
const char *filename,
intptr_t start_at,
char delim,
size_t limit);
```
Same as `fio_string_getdelim_fd` but opens and closes the file.
---
## Memory Helpers
#### `fio_string_capa4len`
```c
FIO_IFUNC size_t fio_string_capa4len(size_t new_len);
```
Returns a 16-byte-aligned capacity for `new_len`.
#### Default Allocator Callbacks
```c
#define FIO_STRING_SYS_REALLOC fio_string_sys_reallocate
#define FIO_STRING_REALLOC fio_string_default_reallocate
#define FIO_STRING_ALLOC_COPY fio_string_default_allocate_copy
#define FIO_STRING_ALLOC_KEY fio_string_default_key_alloc
#define FIO_STRING_FREE fio_string_default_free
#define FIO_STRING_FREE2 fio_string_default_free2
#define FIO_STRING_FREE_KEY fio_string_default_free_key
#define FIO_STRING_FREE_NOOP fio_string_default_free_noop
#define FIO_STRING_FREE_NOOP2 fio_string_default_free_noop2
```
Convenience macros pointing to the default callbacks.
#### `fio_string_default_reallocate`
```c
SFUNC int fio_string_default_reallocate(fio_str_info_s *dst, size_t len);
```
Grows `dst` using the current facil.io allocator (`FIO_MEM_REALLOC_`).
#### `fio_string_default_allocate_copy`
```c
SFUNC int fio_string_default_allocate_copy(fio_str_info_s *dest, size_t new_capa);
```
Allocates a new buffer and copies existing data into it.
#### `fio_string_default_free`
```c
SFUNC void fio_string_default_free(void *);
```
Frees a buffer allocated with the default callbacks.
---
## UTF-8 Helpers
#### `fio_string_utf8_valid`
```c
SFUNC bool fio_string_utf8_valid(fio_str_info_s str);
```
Returns `true` if `str` is valid UTF-8.
#### `fio_string_utf8_len`
```c
SFUNC size_t fio_string_utf8_len(fio_str_info_s str);
```
Returns the number of Unicode code points, or `0` if invalid.
#### `fio_string_utf8_valid_code_point`
```c
SFUNC size_t fio_string_utf8_valid_code_point(const void *u8c, size_t buf_len);
```
Returns the byte length of the UTF-8 code point at `u8c` (`1`–`4`), or `0` if invalid.
#### `fio_string_utf8_select`
```c
SFUNC int fio_string_utf8_select(fio_str_info_s str,
intptr_t *pos,
size_t *len);
```
Converts a UTF-8 character range (`pos`, `len`) into raw byte offsets. Returns `-1` on error and sets `pos` to `-1` if the string is invalid before the selection.
---
## Comparison Helpers
#### `fio_string_is_greater_buf`
```c
SFUNC int fio_string_is_greater_buf(fio_buf_info_s a, fio_buf_info_s b);
```
Returns `1` if `a` is lexicographically greater than `b`; `0` otherwise (including equality).
#### `fio_string_is_greater`
```c
FIO_IFUNC int fio_string_is_greater(fio_str_info_s a, fio_str_info_s b);
```
Same comparison for `fio_str_info_s`.
---
## Binary String (`fio_bstr`)
A reference-counted, copy-on-write, NUL-terminated binary string. The public pointer points at the first character; metadata lives just before it.
#### `fio_bstr_reserve`
```c
FIO_IFUNC char *fio_bstr_reserve(char *bstr, size_t len);
```
Ensures at least `len` additional bytes are available.
#### `fio_bstr_copy`
```c
FIO_IFUNC char *fio_bstr_copy(char *bstr);
```
Returns a shallow copy, incrementing the reference count. Falls back to a real copy if the reference count overflows.
#### `fio_bstr_free`
```c
FIO_IFUNC void fio_bstr_free(char *bstr);
```
Decrements the reference count and frees memory when it reaches zero.
#### `fio_bstr_info` / `fio_bstr_buf` / `fio_bstr_len` / `fio_bstr_len_set`
```c
FIO_IFUNC fio_str_info_s fio_bstr_info(const char *bstr);
FIO_IFUNC fio_buf_info_s fio_bstr_buf(const char *bstr);
FIO_IFUNC size_t fio_bstr_len(const char *bstr);
FIO_IFUNC char *fio_bstr_len_set(char *bstr, size_t len);
```
Inspect or resize the string. `fio_bstr_len_set` may reallocate if the string is shared or too small.
#### `fio_bstr_write` and Friends
```c
FIO_IFUNC char *fio_bstr_write(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_replace(char *bstr, intptr_t start_pos,
size_t overwrite_len, const void *src,
size_t len);
FIO_IFUNC char *fio_bstr_write2(char *bstr, const fio_string_write_s srcs[]);
#define fio_bstr_write2(bstr, ...) \
fio_bstr_write2(bstr, (fio_string_write_s[]){__VA_ARGS__, {0}})
FIO_IFUNC char *fio_bstr_write_i(char *bstr, int64_t num);
FIO_IFUNC char *fio_bstr_write_u(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_hex(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_bin(char *bstr, uint64_t num);
FIO_IFUNC char *fio_bstr_write_escape(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_unescape(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_base64enc(char *bstr, const void *src,
size_t len, uint8_t url_encoded);
FIO_IFUNC char *fio_bstr_write_base64dec(char *bstr, const void *src, size_t len);
FIO_IFUNC char *fio_bstr_write_url_enc(char *bstr, const void *data, size_t len);
FIO_IFUNC char *fio_bstr_write_url_dec(char *bstr, const void *encoded, size_t len);
FIO_IFUNC char *fio_bstr_write_html_escape(char *bstr, const void *raw, size_t len);
FIO_IFUNC char *fio_bstr_write_html_unescape(char *bstr, const void *escaped,
size_t len);
FIO_IFUNC char *fio_bstr_readfd(char *bstr, int fd, intptr_t start_at,
intptr_t limit);
FIO_IFUNC char *fio_bstr_readfile(char *bstr, const char *filename,
intptr_t start_at, intptr_t limit);
FIO_IFUNC char *fio_bstr_getdelim_file(char *bstr, const char *filename,
intptr_t start_at, char delim,
size_t limit);
FIO_IFUNC char *fio_bstr_getdelim_fd(char *bstr, int fd, intptr_t start_at,
char delim, size_t limit);
FIO_IFUNC char *fio_bstr_printf(char *bstr, const char *format, ...);
```
Every `fio_bstr_*` function returns the possibly-reallocated string pointer. Always assign the result back.
#### `fio_bstr_is_greater` / `fio_bstr_is_eq` / `fio_bstr_is_eq2info` / `fio_bstr_is_eq2buf`
```c
FIO_SFUNC int fio_bstr_is_greater(const char *a, const char *b);
FIO_SFUNC int fio_bstr_is_eq(const char *a_, const char *b_);
FIO_SFUNC int fio_bstr_is_eq2info(const char *a_, fio_str_info_s b);
FIO_SFUNC int fio_bstr_is_eq2buf(const char *a_, fio_buf_info_s b);
```
Comparison helpers.
#### `fio_bstr_reallocate`
```c
SFUNC int fio_bstr_reallocate(fio_str_info_s *dest, size_t len);
```
Default reallocate callback for `fio_bstr`.
---
## Key String (`fio_keystr_s`)
A compact key type used by hash maps. Small strings are embedded in the struct; larger ones store a pointer and length. Not NUL-terminated.
#### `fio_keystr_tmp`
```c
FIO_IFUNC fio_keystr_s fio_keystr_tmp(const char *buf, uint32_t len);
```
Returns a temporary key referencing `buf`/`len`.
#### `fio_keystr_init`
```c
FIO_SFUNC fio_keystr_s fio_keystr_init(fio_str_info_s str,
void *(*alloc_func)(size_t len));
```
Copies `str` into a persistent key. Strings short enough are embedded; otherwise `alloc_func` is used. Pass `capa == FIO_KEYSTR_CONST` to store a borrowed pointer.
#### `fio_keystr_destroy`
```c
FIO_SFUNC void fio_keystr_destroy(fio_keystr_s *key,
void (*free_func)(void *, size_t));
```
Releases a key allocated by `fio_keystr_init`.
#### `fio_keystr_buf` / `fio_keystr_info` / `fio_keystr_is_eq` / `fio_keystr_is_eq2` / `fio_keystr_is_eq3` / `fio_keystr_hash`
```c
FIO_IFUNC fio_buf_info_s fio_keystr_buf(fio_keystr_s *str);
FIO_IFUNC fio_str_info_s fio_keystr_info(fio_keystr_s *str);
FIO_IFUNC int fio_keystr_is_eq(fio_keystr_s a, fio_keystr_s b);
FIO_IFUNC int fio_keystr_is_eq2(fio_keystr_s a_, fio_str_info_s b);
FIO_IFUNC int fio_keystr_is_eq3(fio_keystr_s a_, fio_buf_info_s b);
FIO_IFUNC uint64_t fio_keystr_hash(fio_keystr_s a);
```
Inspect, compare, and hash key strings.
#### `FIO_KEYSTR_CONST`
```c
#define FIO_KEYSTR_CONST ((size_t)-1LL)
```
Magic `capa` value for `fio_keystr_init` that keeps a borrowed pointer instead of allocating.
---
## Example
```c
#define FIO_STR
#include "fio-stl.h"
int my_realloc(fio_str_info_s *dest, size_t len) {
size_t capa = fio_string_capa4len(len);
char *tmp = realloc(dest->buf, capa);
if (!tmp) return -1;
dest->buf = tmp;
dest->capa = capa;
return 0;
}
int main(void) {
char buf[32];
fio_str_info_s str = FIO_STR_INFO3(buf, 0, 32);
fio_string_write(&str, my_realloc, "answer: ", 8);
fio_string_write_i(&str, my_realloc, 42);
fprintf(stderr, "%s\n", str.buf);
char *b = NULL;
b = fio_bstr_write(b, "hello ", 6);
b = fio_bstr_write(b, "world", 5);
fprintf(stderr, "%s (len=%zu)\n", b, fio_bstr_len(b));
fio_bstr_free(b);
return 0;
}
```
------------------------------------------------------------
# GFM Markdown Parser
```c
#define FIO_GFM
#include "fio-stl.h"
```
A flat-state, non-recursive parser for GitHub Flavored Markdown. It emits block and inline events through three callbacks — push, write, pop — without allocating heap memory itself. `fio_gfm_parse` is the only public entry point.
The parser depends on `FIO_ENTITY` for HTML entity decoding.
---
## Configuration Macros
#### `FIO_GFM_MAX_DEPTH`
```c
#define FIO_GFM_MAX_DEPTH 255
```
Maximum container nesting depth. Must fit in a byte (≤ 255).
#### `FIO_GFM_MAX_TABLE_COLUMNS`
```c
#define FIO_GFM_MAX_TABLE_COLUMNS 64
```
Maximum table columns. Alignment is bit-packed, so four columns fit in one byte.
#### `FIO_GFM_REF_CACHE_SIZE`
```c
#define FIO_GFM_REF_CACHE_SIZE 128
```
Maximum reference definitions held in the fast inline cache.
---
## Error Codes
#### `FIO_GFM_ERR_GENERIC`, `FIO_GFM_ERR_DEPTH`, `FIO_GFM_ERR_INPUT`
```c
#define FIO_GFM_ERR_GENERIC -1
#define FIO_GFM_ERR_DEPTH -2
#define FIO_GFM_ERR_INPUT -3
```
Negative error codes used internally and returned through `fio_gfm_parse`.
---
## Flags
#### `FIO_GFM_F_TIGHT`
```c
#define FIO_GFM_F_TIGHT ((uint8_t)1U << 0)
```
The list is tight (no blank lines between items).
#### `FIO_GFM_F_LOOSE_SEEN`
```c
#define FIO_GFM_F_LOOSE_SEEN ((uint8_t)1U << 1)
```
A blank line was seen between list items.
#### `FIO_GFM_F_TASK`
```c
#define FIO_GFM_F_TASK ((uint8_t)1U << 2)
```
The list item is a task-list item.
#### `FIO_GFM_F_TASK_CHECKED`
```c
#define FIO_GFM_F_TASK_CHECKED ((uint8_t)1U << 3)
```
The task-list marker is checked (`[x]`).
---
## Types
#### `fio_gfm_type_e`
```c
typedef enum {
FIO_GFM_PARAGRAPH = 1,
FIO_GFM_HEADING,
FIO_GFM_THEMATIC_BREAK,
FIO_GFM_BLOCKQUOTE,
FIO_GFM_LIST_UNORDERED,
FIO_GFM_LIST_ORDERED,
FIO_GFM_LIST_ITEM,
FIO_GFM_CODE_BLOCK,
FIO_GFM_HTML_BLOCK,
FIO_GFM_TABLE,
FIO_GFM_TABLE_ROW,
FIO_GFM_TABLE_CELL,
FIO_GFM_EMPHASIS,
FIO_GFM_STRONG,
FIO_GFM_STRIKETHROUGH,
FIO_GFM_LINK,
FIO_GFM_TEXT,
FIO_GFM_SOFT_BREAK,
FIO_GFM_HARD_BREAK,
FIO_GFM_CODE_SPAN,
FIO_GFM_IMAGE,
FIO_GFM_AUTOLINK,
FIO_GFM_INLINE_HTML,
FIO_GFM_FOOTNOTE_REF,
} fio_gfm_type_e;
```
Event type. Values `1`–`12` are block sections; `13`–`16` are inline sections; `17`–`23` are text and leaf content emitted with `write`.
#### `fio_gfm_align_e`
```c
typedef enum {
FIO_GFM_ALIGN_NONE = 0,
FIO_GFM_ALIGN_LEFT,
FIO_GFM_ALIGN_RIGHT,
FIO_GFM_ALIGN_CENTER,
} fio_gfm_align_e;
```
Table cell alignment.
#### `fio_gfm_event_s`
```c
typedef struct {
void *udata;
fio_buf_info_s source;
fio_buf_info_s text;
fio_buf_info_s marker;
fio_buf_info_s info;
fio_buf_info_s destination;
fio_buf_info_s title;
fio_buf_info_s reference;
uint32_t list_start;
uint16_t columns;
uint16_t column;
uint8_t type;
uint8_t heading_level;
uint8_t flags;
uint8_t align;
uint8_t padding;
} fio_gfm_event_s;
```
Event passed to every callback.
**Fields:**
- `udata` — live user pointer; the parser copies it back after each callback.
- `source` — full source slice for this event.
- `text` — literal text / label / code content.
- `marker` — marker slice (`#`, fence, list bullet, etc.).
- `info` — fenced code info string (language tag).
- `destination` — link/image/autolink URL.
- `title` — link/image title.
- `reference` — reference label for ref-style links/images.
- `list_start` — ordered list start number.
- `columns` / `column` — table column count and cell index.
- `type` — `fio_gfm_type_e` value.
- `heading_level` — `1`–`6` for headings.
- `flags` — `FIO_GFM_F_*`.
- `align` — `fio_gfm_align_e` for table cells.
- `padding` — virtual leading spaces from tab expansion.
#### `fio_gfm_callbacks_s`
```c
typedef struct {
int (*push)(fio_gfm_event_s *e);
int (*write)(fio_gfm_event_s *e);
int (*pop)(fio_gfm_event_s *e);
} fio_gfm_callbacks_s;
```
Parser callbacks. All are optional. Return `0` to continue, non-zero to abort. Positive values are user errors; negative values are reserved for parser errors.
---
## Parsing
#### `fio_gfm_parse`
```c
SFUNC size_t fio_gfm_parse(const fio_gfm_callbacks_s *callbacks,
void *udata,
fio_buf_info_s source);
```
Parses a complete Markdown document and emits events. The document must be fully available in `source`; streaming is not supported.
**Parameters:**
- `callbacks` — pointer to callback struct.
- `udata` — user pointer copied into every event; callbacks may update it.
- `source` — full document bytes.
**Returns:** the number of bytes consumed. If this equals `source.len`, parsing completed. A smaller value means a callback aborted or a parser error occurred.
---
## Example
```c
#define FIO_GFM
#include "fio-stl.h"
static int on_push(fio_gfm_event_s *e) {
fprintf(stderr, "open type=%u\n", e->type);
return 0;
}
static int on_write(fio_gfm_event_s *e) {
fprintf(stderr, "text: %.*s\n", (int)e->text.len, e->text.buf);
return 0;
}
static int on_pop(fio_gfm_event_s *e) {
fprintf(stderr, "close type=%u\n", e->type);
return 0;
}
int main(void) {
fio_gfm_callbacks_s cb = {
.push = on_push,
.write = on_write,
.pop = on_pop,
};
fio_buf_info_s src = FIO_BUF_INFO2("# Hello\n\nworld.\n", 16);
size_t n = fio_gfm_parse(&cb, NULL, src);
fprintf(stderr, "consumed %zu of %zu bytes\n", n, src.len);
return 0;
}
```
------------------------------------------------------------
# POSIX Portable Polling API
```c
#define FIO_POLL
#include "fio-stl.h"
```
A small abstraction over `poll`, `epoll`, and `kqueue`. Define `FIO_POLL_ENGINE_POLL`, `FIO_POLL_ENGINE_EPOLL`, or `FIO_POLL_ENGINE_KQUEUE` before inclusion to force a backend; otherwise the best available engine is selected automatically.
The API is the same for every backend. Backend-specific details live in:
- [`102 poll poll.md`](https://facil.io/edge/posix-portable-polling-api/)
- [`102 poll epoll.md`](https://facil.io/edge/posix-polling-epoll-backend/)
- [`102 poll kqueue.md`](https://facil.io/edge/posix-polling-kqueue-backend/)
---
## Configuration Macros
#### `FIO_POLL_POSSIBLE_FLAGS`
```c
#define FIO_POLL_POSSIBLE_FLAGS (POLLIN | POLLOUT | POLLPRI)
```
Mask of event flags the polling layer recognizes.
#### `FIO_POLL_MAX_EVENTS`
```c
#define FIO_POLL_MAX_EVENTS 128
```
Maximum events returned in one `fio_poll_review` call. Relevant for epoll and kqueue. Larger on 32-bit platforms.
---
## Types
#### `fio_poll_s`
```c
typedef struct fio_poll_s fio_poll_s;
```
Opaque polling object. The actual layout depends on the selected backend.
#### `fio_poll_settings_s`
```c
typedef struct {
void (*on_data)(void *udata);
void (*on_ready)(void *udata);
void (*on_close)(void *udata);
} fio_poll_settings_s;
```
Callback settings. Each callback receives the `udata` pointer supplied when the file descriptor was monitored.
- `on_data` — data is available to read.
- `on_ready` — the socket is ready to write.
- `on_close` — connection closed or an error occurred.
---
## Functions
#### `fio_poll_engine`
```c
FIO_IFUNC const char *fio_poll_engine(void);
```
Returns the name of the active backend (`"epoll"`, `"kqueue"`, or `"poll"`) as a constant string.
#### `fio_poll_init`
```c
FIO_IFUNC void fio_poll_init(fio_poll_s *p, fio_poll_settings_s settings);
#define fio_poll_init(p, ...) \
fio_poll_init((p), (fio_poll_settings_s){__VA_ARGS__})
```
Initializes the polling object. The macro form accepts designated initializers.
**Parameters:**
- `p` — pointer to the polling object.
- `settings` — callback configuration.
```c
fio_poll_init(&poll,
.on_data = on_data,
.on_ready = on_ready,
.on_close = on_close);
```
#### `fio_poll_destroy`
```c
FIO_IFUNC void fio_poll_destroy(fio_poll_s *p);
```
Frees backend resources held by the polling object.
#### `fio_poll_monitor`
```c
SFUNC int fio_poll_monitor(fio_poll_s *p,
fio_socket_i fd,
void *udata,
unsigned short flags);
```
Adds `fd` to the poll set, updates its `udata`, or adds events. Recognized flags are `POLLIN`, `POLLOUT`, and `POLLPRI`; other flags may be ignored. Monitoring is one-shot: once an event fires, it is removed until re-armed.
**Returns:** `0` on success, `-1` on error.
#### `fio_poll_review`
```c
SFUNC int fio_poll_review(fio_poll_s *p, size_t timeout);
```
Waits up to `timeout` milliseconds for events and dispatches callbacks. Adding a file descriptor from another thread while `fio_poll_review` is running will not include that descriptor until the next call.
**Returns:** the number of events dispatched, or `-1` on error.
#### `fio_poll_forget`
```c
SFUNC int fio_poll_forget(fio_poll_s *p, fio_socket_i fd);
```
Removes `fd` from the poll set.
**Returns:** `0` on success, `-1` on error.
---
## Example
```c
#define FIO_POLL
#include "fio-stl.h"
static void on_data(void *udata) { fprintf(stderr, "readable\n"); }
static void on_ready(void *udata) { fprintf(stderr, "writable\n"); }
static void on_close(void *udata) { fprintf(stderr, "closed\n"); }
int main(void) {
fio_poll_s p;
fio_poll_init(&p,
.on_data = on_data,
.on_ready = on_ready,
.on_close = on_close);
fio_poll_monitor(&p, some_fd, NULL, POLLIN);
for (int i = 0; i < 10; ++i)
fio_poll_review(&p, 1000);
fio_poll_destroy(&p);
return 0;
}
```
------------------------------------------------------------
# POSIX Polling — epoll Backend
```c
#define FIO_POLL_ENGINE_EPOLL
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of Linux `epoll`. Normally selected automatically on Linux; define `FIO_POLL_ENGINE_EPOLL` to force it.
The public API is documented in [`102 poll api.md`](https://facil.io/edge/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
struct pollfd fds[2];
int fd[2];
};
```
Two internal `epoll` file descriptors are used: one for `POLLOUT` readiness and one for `POLLIN`/error events. A `pollfd` pair wakes the internal `poll` used to wait on both epoll FDs at once.
#### `fio_poll_engine`
Returns `"epoll"`.
#### Fork Safety
A state callback re-creates the epoll FDs in the child process after `fork`.
---
## Notes
- Events are monitored in one-shot mode (`EPOLLONESHOT`). After an event fires, the descriptor must be re-armed with `fio_poll_monitor`.
- Error and hang-up events are dispatched through the read-side epoll FD and delivered to `on_close`.
- `EPOLLRDHUP` and `EPOLLHUP` are always included in the monitored events.
------------------------------------------------------------
# POSIX Polling — kqueue Backend
```c
#define FIO_POLL_ENGINE_KQUEUE
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of BSD/macOS `kqueue`. Normally selected automatically when `` is available; define `FIO_POLL_ENGINE_KQUEUE` to force it.
The public API is documented in [`102 poll api.md`](https://facil.io/edge/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
int fd;
};
```
A single kqueue descriptor is used for both read and write filters.
#### `fio_poll_engine`
Returns `"kqueue"`.
#### Fork Safety
A state callback re-creates the kqueue descriptor in the child process after `fork`.
---
## Notes
- Read and write filters are registered with `EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT`.
- `EV_EOF` and `EV_ERROR` flags are routed to `on_close`.
- `fio_poll_review` uses a `timespec` built from the millisecond timeout.
------------------------------------------------------------
# POSIX Polling — poll Backend
```c
#define FIO_POLL_ENGINE_POLL
#define FIO_POLL
#include "fio-stl.h"
```
Implementation of the portable polling API on top of POSIX `poll()` / Windows `WSAPoll`. This is the fallback backend and is selected automatically when neither epoll nor kqueue is available.
The public API is documented in [`102 poll api.md`](https://facil.io/edge/posix-portable-polling-api/). This file describes backend-specific behavior.
---
## Backend Details
#### `struct fio_poll_s`
```c
struct fio_poll_s {
fio_poll_settings_s settings;
fio___poll_map_s map;
FIO___LOCK_TYPE lock;
};
```
The poll backend keeps monitored descriptors in an internal imap (`fio___poll_map_s`) and snapshots its armed flags before each `poll()` call. Descriptors added or re-armed during `fio_poll_review` update the retained map directly; surviving one-shot flags are restored when the call returns.
`fio_poll_forget` removes the descriptor from the retained map, which also prevents any surviving snapshot flags from being restored. It is not a cancellation guarantee: an event already returned by an in-flight `poll()` call may still be dispatched. This matches the epoll/kqueue backends, whose in-hand event lists always dispatch. As on every backend, `udata` lifetime during dispatch is the caller's responsibility (the facil.io IO layer only forgets from the reactor thread, never concurrently with a review).
One-shot flag accounting strips whole event groups: `WSAPoll` reports sub-band bits (e.g. `POLLRDNORM`) while `POLLIN`/`POLLOUT` are supersets, so a raw bitwise strip would leave band bits (e.g. `POLLRDBAND`) armed on Windows. Fired `POLLIN`/`POLLOUT` groups are therefore stripped in full.
#### `fio_poll_engine`
Returns `"poll"`.
#### `fio_poll_close_all`
```c
SFUNC void fio_poll_close_all(fio_poll_s *p);
```
Additional helper available only in the poll backend. Closes every monitored socket and calls `on_close` for each one. Useful during shutdown.
---
## Notes
- `POLLRDHUP` is used when available; otherwise the backend relies on `POLLHUP`, `POLLERR`, and `POLLNVAL` for close/error detection.
- On Windows, `POLLPRI` is omitted because `WSAPoll` rejects it.
- Fired events are stripped from the descriptor’s flags (one-shot semantics). Surviving flags are restored to retained entries after `poll()` returns; concurrent removals (which delete the retained entry) prevent that restoration.
------------------------------------------------------------
# Task and Timer Queues
```c
#define FIO_QUEUE
#include "fio-stl.h"
```
A thread-safe task queue built from linked ring buffers, plus a timer queue that moves due events into a task queue. Worker threads can be attached to drain the queue in the background.
---
## Configuration
#### `FIO_QUEUE_TASKS_PER_ALLOC`
```c
#define FIO_QUEUE_TASKS_PER_ALLOC 168 /* 338 on 32-bit */
```
Tasks per ring-buffer chunk. Default is chosen so `fio_queue_s` fits in one page. Must not exceed 65535.
---
## Task Queue Types
#### `fio_queue_task_s`
```c
typedef struct {
void (*fn)(void *, void *);
void *udata1;
void *udata2;
} fio_queue_task_s;
```
A single task. `fn` receives both `udata` pointers. Tasks with `fn == NULL` are ignored.
#### `fio_queue_s`
```c
typedef struct {
fio___task_ring_s *r;
fio___task_ring_s *w;
uint32_t count;
FIO___LOCK_TYPE lock;
FIO_LIST_NODE consumers;
fio___task_ring_s mem;
} fio_queue_s;
```
Queue object. Treat as opaque. Allocate on the stack or with `fio_queue_new`.
---
## Task Queue API
#### `FIO_QUEUE_STATIC_INIT`
```c
#define FIO_QUEUE_STATIC_INIT(queue) /* ... */
```
Static initializer for a global queue. Prefer `fio_queue_init` at runtime when possible — it initializes only the bytes that matter.
#### `fio_queue_init`
```c
FIO_IFUNC void fio_queue_init(fio_queue_s *q);
```
Initializes `q` in place.
#### `fio_queue_destroy`
```c
SFUNC void fio_queue_destroy(fio_queue_s *q);
```
Frees ring buffers, stops and joins workers, and re-initializes `q`. Pending tasks are performed only if the queue has workers (see `fio_queue_workers_join`); otherwise they are discarded without being performed. After destruction the queue may be reused, but on some platforms the lock may need explicit re-initialization.
#### `fio_queue_new`
```c
SFUNC fio_queue_s *fio_queue_new(void);
```
Allocates and initializes a queue on the heap.
#### `fio_queue_free`
```c
SFUNC void fio_queue_free(fio_queue_s *q);
```
Destroys `q` and frees the queue object itself.
#### `fio_queue_push`
```c
SFUNC int fio_queue_push(fio_queue_s *q, fio_queue_task_s task);
#define fio_queue_push(q, ...) \
fio_queue_push((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the tail of the queue. The macro accepts named arguments.
**Returns:** `0` on success, `-1` on memory error.
```c
fio_queue_push(&q, .fn = my_task, .udata1 = arg);
```
#### `fio_queue_push_urgent`
```c
SFUNC int fio_queue_push_urgent(fio_queue_s *q, fio_queue_task_s task);
#define fio_queue_push_urgent(q, ...) \
fio_queue_push_urgent((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the head of the queue (LIFO).
**Returns:** `0` on success, `-1` on memory error.
#### `fio_queue_pop`
```c
SFUNC fio_queue_task_s fio_queue_pop(fio_queue_s *q);
```
Removes and returns the next task (FIFO). The returned task has `fn == NULL` if the queue was empty.
#### `fio_queue_perform`
```c
SFUNC int fio_queue_perform(fio_queue_s *q);
```
Pops and performs one task. Returns `-1` if the queue was empty.
#### `fio_queue_perform_all`
```c
SFUNC void fio_queue_perform_all(fio_queue_s *q);
```
Performs every task currently in the queue.
#### `fio_queue_count`
```c
FIO_IFUNC uint32_t fio_queue_count(fio_queue_s *q);
```
Returns the number of pending tasks.
---
## Worker Threads
#### `fio_queue_workers_add`
```c
SFUNC int fio_queue_workers_add(fio_queue_s *q, size_t count);
```
Spawns `count` consumer threads that automatically perform tasks as they arrive. Threads sleep on a condition variable when idle.
**Returns:** `0` on success, `-1` on thread creation failure.
#### `fio_queue_workers_stop`
```c
SFUNC size_t fio_queue_workers_stop(fio_queue_s *q);
```
Signals all workers to stop. Returns immediately without waiting.
Pending tasks may remain in the queue (they can be performed later using `fio_queue_perform_all` or discarded by `fio_queue_destroy`).
**Returns:** the number of worker groups signaled (one per successful `fio_queue_workers_add` call still attached to the queue).
#### `fio_queue_workers_join`
```c
SFUNC void fio_queue_workers_join(fio_queue_s *q);
```
Signals workers to stop and blocks until they terminate.
If the queue had workers, any pending tasks are performed before the function returns (so `fio_queue_destroy`, which calls `fio_queue_workers_join`, drains a queue that has workers). A queue without workers is not drained: `fio_queue_destroy` on such a queue discards pending tasks without performing them.
#### `fio_queue_workers_wake`
```c
SFUNC void fio_queue_workers_wake(fio_queue_s *q);
```
Wakes all workers to check for new tasks. Called automatically on push.
---
## Timer Queue Types
#### `fio_timer_queue_s`
```c
typedef struct {
fio___timer_event_s *next;
FIO___LOCK_TYPE lock;
} fio_timer_queue_s;
```
Opaque timer queue.
#### `FIO_TIMER_QUEUE_INIT`
```c
#define FIO_TIMER_QUEUE_INIT /* ... */
```
Static initializer for a timer queue.
#### `fio_timer_schedule_args_s`
```c
typedef struct {
int (*fn)(void *, void *);
void *udata1;
void *udata2;
void (*on_finish)(void *, void *);
uint32_t every;
int32_t repetitions;
int64_t start_at;
} fio_timer_schedule_args_s;
```
Timer schedule arguments.
**Members:**
- `fn` — callback. Return non-zero to stop the timer.
- `udata1`, `udata2` — opaque data passed to `fn` and `on_finish`.
- `on_finish` — called when the timer stops.
- `every` — interval in milliseconds.
- `repetitions` — repeat count; `-1` means forever.
- `start_at` — base time in milliseconds; `0` uses `fio_time_milli()`.
---
## Timer Queue API
#### `fio_timer_schedule`
```c
SFUNC void fio_timer_schedule(fio_timer_queue_s *timer_queue,
fio_timer_schedule_args_s args);
#define fio_timer_schedule(timer_queue, ...) \
fio_timer_schedule((timer_queue), (fio_timer_schedule_args_s){__VA_ARGS__})
```
Adds a timed event to the timer queue. The macro accepts named arguments.
```c
fio_timer_schedule(&timers,
.fn = tick,
.udata1 = ctx,
.every = 1000,
.repetitions = -1);
```
#### `fio_timer_push2queue`
```c
SFUNC size_t fio_timer_push2queue(fio_queue_s *queue,
fio_timer_queue_s *timer_queue,
int64_t now_in_milliseconds);
```
Moves all due timer events into `queue`. Pass `0` for `now_in_milliseconds` to use `fio_time_milli()`.
**Returns:** number of timers pushed.
#### `fio_timer_next_at`
```c
FIO_IFUNC int64_t fio_timer_next_at(fio_timer_queue_s *timer_queue);
```
Returns the due time of the next event, or `INT64_MAX` if the queue is empty.
#### `fio_timer_destroy`
```c
SFUNC void fio_timer_destroy(fio_timer_queue_s *timer_queue);
```
Cancels all pending timers. Do not free the timer queue while timer tasks may still be queued in a `fio_queue_s`, because repeating timers reschedule themselves.
---
## Example
```c
#define FIO_QUEUE
#include "fio-stl.h"
void work(void *a, void *b) {
(void)a; (void)b;
fprintf(stderr, "working\n");
}
int tick(void *a, void *b) {
(void)a; (void)b;
fprintf(stderr, "tick\n");
return 0;
}
int main(void) {
fio_queue_s q = {0};
fio_queue_init(&q);
fio_queue_push(&q, .fn = work);
fio_queue_perform(&q);
fio_timer_queue_s t = FIO_TIMER_QUEUE_INIT;
fio_timer_schedule(&t, .fn = tick, .every = 100, .repetitions = 3);
fio_timer_push2queue(&q, &t, 0);
fio_queue_perform_all(&q);
fio_timer_destroy(&t);
fio_queue_destroy(&q);
return 0;
}
```
------------------------------------------------------------
# Packet Data Stream
```c
#define FIO_STREAM
#include "fio-stl.h"
```
A packet-based byte stream for buffering data from memory buffers and file descriptors. It is useful when writes are partial: add packets to the stream, read the next contiguous chunk into or from a scratch buffer, write what you can, then advance by the number of bytes written.
For the low-level helpers commonly used with streams, see [`004 files.md`](https://facil.io/edge/file-helpers/), [`004 sock.md`](https://facil.io/edge/basic-socket-helpers/), and [`102 poll api.md`](https://facil.io/edge/posix-portable-polling-api/).
---
## Configuration Macros
#### `FIO_STREAM_COPY_PER_PACKET`
```c
#define FIO_STREAM_COPY_PER_PACKET 98304
```
Maximum copied payload per packet. Larger copied buffers are split into multiple packets so memory can be released progressively. Override before including the implementation.
#### `FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`
```c
#define FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN 116 /* 8 in DEBUG */
```
Small buffers are copied even when `copy_buffer == 0`, improving locality and avoiding tiny external references.
---
## Types
#### `fio_stream_s`
```c
typedef struct {
fio_stream_packet_s *next;
fio_stream_packet_s **pos;
size_t consumed;
size_t length;
} fio_stream_s;
```
Stream object. Treat the fields as private. Allocate on the stack with `FIO_STREAM_INIT` or on the heap with `fio_stream_new`.
#### `fio_stream_packet_s`
```c
typedef struct fio_stream_packet_s fio_stream_packet_s;
```
Opaque packed data node. Packets are prepared separately, then transferred to a stream with `fio_stream_add`.
---
## Lifecycle
#### `FIO_STREAM_INIT`
```c
#define FIO_STREAM_INIT(s) { .next = NULL, .pos = &(s).next }
```
Initializer for an in-place stream.
```c
fio_stream_s stream = FIO_STREAM_INIT(stream);
```
#### `fio_stream_new`
```c
fio_stream_s *fio_stream_new(void);
```
Allocates and initializes a heap stream.
**Returns:** stream pointer, or `NULL` on allocation failure.
Not declared when `FIO_REF_CONSTRUCTOR_ONLY` is defined.
#### `fio_stream_free`
```c
int fio_stream_free(fio_stream_s *stream);
```
Destroys `stream`, frees the stream object itself, and returns `0`.
Not declared when `FIO_REF_CONSTRUCTOR_ONLY` is defined.
#### `fio_stream_destroy`
```c
void fio_stream_destroy(fio_stream_s *stream);
```
Frees all queued packets and re-initializes the stream object. Safe to call with `NULL`.
---
## Packing Data
#### `fio_stream_pack_data`
```c
fio_stream_packet_s *fio_stream_pack_data(void *buf,
size_t len,
size_t offset,
uint8_t copy_buffer,
void (*dealloc_func)(void *));
```
Packs `len` bytes starting at `((char *)buf + offset)`.
- If `copy_buffer` is non-zero, or `len < FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`, bytes are copied into stream-owned packets.
- Otherwise the packet references `buf` and calls `dealloc_func(buf)` when the packet is freed, if `dealloc_func` is not `NULL`.
- Copied data larger than `FIO_STREAM_COPY_PER_PACKET` is split into multiple packets.
**Returns:** packet pointer, or `NULL` if `buf == NULL`, `len == 0`, the length is too large for the packet format, or allocation fails. If `dealloc_func` is provided, it is called on both success-after-copy and failure.
#### `fio_stream_pack_fd`
```c
fio_stream_packet_s *fio_stream_pack_fd(int fd,
size_t len,
size_t offset,
uint8_t keep_open);
```
Packs bytes from an open file descriptor. Reads are performed later by `fio_stream_read` using `fio_fd_read`.
- `len == 0` auto-detects the remaining file size with `fio_fd_size(fd)`.
- `offset` is the starting file offset.
- If `keep_open == 0`, the descriptor is closed when the packet is freed, or on pack failure after ownership was accepted.
- If `keep_open != 0`, the caller remains responsible for closing `fd`.
**Returns:** packet pointer, or `NULL` on invalid input, size detection failure / oversized auto-detected size when `len == 0`, or allocation failure.
#### `fio_stream_add`
```c
void fio_stream_add(fio_stream_s *stream, fio_stream_packet_s *packet);
```
Appends `packet` to `stream` and transfers packet ownership to the stream. If `stream` or `packet` is `NULL`, the packet is freed.
This is not thread-safe.
#### `fio_stream_pack_free`
```c
void fio_stream_pack_free(fio_stream_packet_s *packet);
```
Frees a packet that was not added to a stream. Do not call this after `fio_stream_add` succeeds.
---
## Reading and Consuming
#### `fio_stream_read`
```c
void fio_stream_read(fio_stream_s *stream, char **buf, size_t *len);
```
Reads the next available bytes without consuming them.
Before the call, `*buf` must point to a scratch buffer with at least `*len` bytes. This buffer is required when data spans multiple packets or comes from a file descriptor.
After the call:
- On empty stream or error, `*buf == NULL` and `*len == 0`.
- Otherwise `*buf` either still points to the supplied scratch buffer or points directly into stream-owned memory.
- `*len` is updated to the number of readable bytes available at `*buf`.
Reset both `*buf` and `*len` before each read if reusing the same scratch buffer. This is not thread-safe.
#### `fio_stream_advance`
```c
void fio_stream_advance(fio_stream_s *stream, size_t len);
```
Consumes `len` bytes from the stream, freeing fully consumed packets. Usually pass the number of bytes actually written to the destination.
This is not thread-safe.
#### `fio_stream_any`
```c
uint8_t fio_stream_any(fio_stream_s *stream);
```
Returns non-zero when `stream` has pending packets. Returns `0` for `NULL` or empty streams. This is not truly thread-safe.
#### `fio_stream_length`
```c
size_t fio_stream_length(fio_stream_s *stream);
```
Returns the number of bytes waiting in the stream. Call with a valid stream object. This is not truly thread-safe.
---
## Ownership and Thread-Safety
Packing creates a packet owned by the caller. `fio_stream_add` transfers that ownership to the stream. `fio_stream_destroy`, `fio_stream_free`, and `fio_stream_advance` free packets as needed.
For referenced memory packets, the referenced buffer must remain valid until the packet is freed. Pass `copy_buffer != 0` when that lifetime is not guaranteed. For file descriptor packets, keep the descriptor readable until the packet is consumed or destroyed; `keep_open` controls who closes it.
Packing packets can be done before taking the stream lock, but mutating or consuming a single `fio_stream_s` (`add`, `read`, `advance`, and length checks) should be externally synchronized when shared across threads.
---
## Example
```c
#define FIO_STREAM
#include "fio-stl.h"
#include
#include
int main(void) {
fio_stream_s stream = FIO_STREAM_INIT(stream);
char scratch[4096];
char msg[] = "hello, stream\n";
fio_stream_packet_s *p = fio_stream_pack_data(msg, strlen(msg), 0, 1, NULL);
fio_stream_add(&stream, p);
while (fio_stream_any(&stream)) {
char *buf = scratch;
size_t len = sizeof(scratch);
fio_stream_read(&stream, &buf, &len);
if (!len)
break;
size_t written = fwrite(buf, 1, len, stdout);
fio_stream_advance(&stream, written);
if (written != len)
break;
}
fio_stream_destroy(&stream);
return 0;
}
```
### File Descriptor Packet
```c
#define FIO_STREAM
#define FIO_FILES
#include "fio-stl.h"
fio_stream_s stream = FIO_STREAM_INIT(stream);
int fd = fio_filename_open("./asset.bin", O_RDONLY);
fio_stream_packet_s *p = fio_stream_pack_fd(fd, 0, 0, 0); /* stream closes fd */
fio_stream_add(&stream, p);
/* ... read / write packets ... */
fio_stream_destroy(&stream);
```
------------------------------------------------------------
# Markdown to HTML bstr Renderer
```c
#define FIO_MD2HTML
#include "fio-stl.h"
```
A complete-document Markdown / GFM renderer that turns parser events into escaped HTML stored in an owned `fio_bstr`. Markdown goes in, tags come out. Implemented in [`./103 md2html.h`](https://facil.io/edge/api/103-md2html/).
Defining `FIO_MD2HTML` automatically includes:
- `FIO_STR` for `fio_bstr`
- `FIO_GFM` for Markdown / GFM parsing
### Error Codes
#### `FIO_MD2HTML_ERR_ALLOC`
```c
#ifndef FIO_MD2HTML_ERR_ALLOC
#define FIO_MD2HTML_ERR_ALLOC 1
#endif
```
Internal renderer error used when output allocation fails. Override before inclusion only if you need a different non-zero value.
### Rendering
#### `fio_md2html`
```c
SFUNC char *fio_md2html(char *bstr_target, fio_buf_info_s source);
```
Renders a complete Markdown / GFM document into a `fio_bstr`.
**Parameters:**
- `bstr_target` - existing `fio_bstr` to append to, or `NULL` to allocate a new one
- `source` - Markdown bytes to parse
**Returns:** the output `fio_bstr`, or `NULL` on parser / allocation failure.
**Ownership:** the returned buffer is owned by the caller and should be released with `fio_bstr_free`. If `bstr_target == NULL` and rendering fails, the newly allocated output is freed. If `bstr_target` was provided, ownership stays with the caller.
### Rendering Behavior
From the implementation:
- normal text, code, and attribute values are escaped
- raw Markdown HTML blocks / inline HTML are preserved, with GFM tagfilter applied
- blocked tag names include `iframe`, `noembed`, `noframes`, `plaintext`, `style`, `textarea`, `title`, and `xmp`; HTML blocks only filter `textarea` and `xmp`
- fenced code info strings use the first word as `class="language-..."`
- GFM tables render `...` and open ` ` for body rows
- table cell alignment uses `align="left"`, `align="right"`, or `align="center"`
- GFM task list items render disabled checkbox inputs
- autolinks add `mailto:` for bare email addresses and `http://` for `www.` links
- image alt text is escaped and strips nested link / emphasis markup
### Thread-Safety
The renderer uses caller-provided input and a local renderer state. It is thread-safe as long as separate calls do not mutate the same `bstr_target` at the same time.
### Example
```c
#define FIO_MD2HTML
#include "fio-stl.h"
#include
int main(void) {
char md[] = "# Demo\n\nHello **world**.\n";
char *html = fio_md2html(NULL, FIO_BUF_INFO1(md));
if (!html)
return 1;
fwrite(html, 1, fio_bstr_len(html), stdout);
fio_bstr_free(html);
return 0;
}
```
---
# Mustache-ish Template Engine
```c
#define FIO_MUSTACHE
#include "fio-stl.h"
```
A Mustache-ish template parser and renderer with variables, sections, inverted sections, partials, delimiter changes, comments, and optional YAML front matter callbacks. Logic-less-ish, because C callbacks still get a vote. Implemented in [`./104 mustache.h`](https://facil.io/edge/api/104-mustache/).
Defining `FIO_MUSTACHE` pulls in string / `fio_bstr` helpers as needed.
### Configuration Macros
#### `FIO_MUSTACHE_MAX_DEPTH`
```c
#ifndef FIO_MUSTACHE_MAX_DEPTH
#define FIO_MUSTACHE_MAX_DEPTH 128
#endif
```
Maximum parser / builder nesting depth for sections and partials.
#### `FIO_MUSTACHE_PRESERVE_PADDING`
```c
#ifndef FIO_MUSTACHE_PRESERVE_PADDING
#define FIO_MUSTACHE_PRESERVE_PADDING 0
#endif
```
When enabled, preserves padding for stand-alone variables and partial templates.
#### `FIO_MUSTACHE_LAMBDA_SUPPORT`
```c
#ifndef FIO_MUSTACHE_LAMBDA_SUPPORT
#define FIO_MUSTACHE_LAMBDA_SUPPORT 0
#endif
```
When enabled, stores raw section text for lambda-style section handling through `is_lambda`.
#### `FIO_MUSTACHE_ISOLATE_PARTIALS`
```c
#ifndef FIO_MUSTACHE_ISOLATE_PARTIALS
#define FIO_MUSTACHE_ISOLATE_PARTIALS 1
#endif
```
When enabled, limits partial lookup scope to the context of the partial's section.
#### `FIO_MUSTACHE_SECURE_PATH`
```c
#ifndef FIO_MUSTACHE_SECURE_PATH
#define FIO_MUSTACHE_SECURE_PATH 1
#endif
```
When enabled, skips partial names that attempt `../` path traversal.
### Syntax Quick Map
| Syntax | Meaning |
| --- | --- |
| `{{name}}` | escaped variable |
| `{{{name}}}` | raw variable |
| `{{&name}}` | raw variable |
| `{{#items}}...{{/items}}` | truthy section / array iteration |
| `{{^items}}...{{/items}}` | inverted section |
| `{{>partial}}` | partial template include |
| `{{! comment}}` | comment |
| `{{=<% %>=}}` | delimiter change |
| `{{.}}` | current context |
Dot lookup such as `person.name` is supported by repeatedly calling `get_var` for path segments.
### Types
#### `fio_mustache_s`
```c
typedef struct fio_mustache_s fio_mustache_s;
```
Opaque parsed template object. Internally it is stored as a `fio_bstr` instruction stream.
**Ownership:** `fio_mustache_load` returns an owned template. Free it with `fio_mustache_free`. `fio_mustache_dup` creates a copy; it is not a shared reference counter in this header.
#### `fio_mustache_bargs_s`
```c
typedef struct fio_mustache_bargs_s fio_mustache_bargs_s;
```
Forward declaration for build arguments. The full struct is listed below.
#### `fio_mustache_load_args_s`
```c
typedef struct {
fio_buf_info_s data;
fio_buf_info_s filename;
fio_buf_info_s (*load_file_data)(fio_buf_info_s filename, void *udata);
void (*free_file_data)(fio_buf_info_s file_data, void *udata);
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
void *udata;
} fio_mustache_load_args_s;
```
Load / parse settings.
**Members:**
- `data` - preloaded template bytes
- `filename` - file name, also used as base path for partials
- `load_file_data` - callback that loads file contents
- `free_file_data` - callback that frees loaded file contents
- `on_yaml_front_matter` - called when front matter is found
- `udata` - user data for load callbacks
If both `load_file_data` and `free_file_data` are missing, defaults use `fio_bstr_readfile` and `fio_bstr_free`. If one is custom, provide the matching cleanup. Either `filename` or `data` is required.
#### `fio_mustache_bargs_s`
```c
struct fio_mustache_bargs_s {
void *(*write_text)(void *udata, fio_buf_info_s txt);
void *(*write_text_escaped)(void *udata, fio_buf_info_s raw);
void *(*get_var)(void *ctx, fio_buf_info_s name);
size_t (*array_length)(void *ctx);
void *(*get_var_index)(void *ctx, size_t index);
fio_buf_info_s (*var2str)(void *var);
int (*var_is_truthful)(void *ctx);
void (*release_var)(void *ctx);
int (*is_lambda)(void **udata,
void *ctx,
fio_buf_info_s raw_template_section);
void *ctx;
void *udata;
};
```
Build / render callbacks.
**Members:**
- `write_text` - writes raw template text
- `write_text_escaped` - writes escaped variable text
- `get_var` - returns a value for `name` from `ctx`
- `array_length` - returns array length, or `0` for non-array values
- `get_var_index` - returns an item context by index
- `var2str` - returns a string view for a value
- `var_is_truthful` - returns non-zero for truthy values
- `release_var` - releases values returned by callbacks
- `is_lambda` - handles lambda sections when lambda support is enabled
- `ctx` - root render context
- `udata` - output / user pointer; the final value is returned by `fio_mustache_build`
If both writer callbacks are missing, defaults append to a `fio_bstr` in `udata`. The returned pointer should be freed with `fio_bstr_free`.
### Loading
#### `fio_mustache_load`
```c
SFUNC fio_mustache_s *fio_mustache_load(fio_mustache_load_args_s settings);
#define fio_mustache_load(...) \
fio_mustache_load((fio_mustache_load_args_s){__VA_ARGS__})
```
Loads and parses a template from `settings.data` or `settings.filename`.
If `filename` is provided and `data` is empty, the loader callback is used. If both are provided, `data` is parsed and `filename` is kept for partial path resolution.
**Returns:** a parsed template object, or `NULL` on error.
#### `fio_mustache_free`
```c
SFUNC void fio_mustache_free(fio_mustache_s *m);
```
Frees a parsed template. Accepts `NULL`.
#### `fio_mustache_dup`
```c
SFUNC fio_mustache_s *fio_mustache_dup(fio_mustache_s *m);
```
Copies a parsed template using `fio_bstr_copy`.
**Returns:** the copied template, or `NULL` if `m == NULL`.
### Rendering
#### `fio_mustache_build`
```c
SFUNC void *fio_mustache_build(fio_mustache_s *m, fio_mustache_bargs_s);
#define fio_mustache_build(m, ...) \
fio_mustache_build((m), ((fio_mustache_bargs_s){__VA_ARGS__}))
```
Renders `m` with the supplied context and callbacks.
**Returns:** the final `udata` value. If `m == NULL`, returns `args.udata` unchanged.
During rendering:
- escaped variables call `write_text_escaped`
- raw variables call `write_text`
- sections render once for truthy non-arrays, or once per array item
- inverted sections render when `var_is_truthful` returns zero
- values returned from lookup / array callbacks are released with `release_var`
### Partials and Files
Partials are loaded by name through `load_file_data`. The implementation tries each name with these suffixes:
1. `.mustache`
2. `.html`
3. no suffix
Relative partials are searched against the including template's path chain, then as provided. With `FIO_MUSTACHE_SECURE_PATH`, `../` partials are skipped.
### YAML Front Matter
If a template begins with `---` followed by a line break, the parser scans until a closing `---` line and calls:
```c
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
```
The front matter bytes are not rendered. The engine does not parse YAML for you. It has enough hobbies.
### Thread-Safety
Parsed templates are immutable during `fio_mustache_build`, so separate builds can use the same template as long as your callbacks and contexts are safe. Loading and freeing are caller-owned operations.
### Example
```c
#define FIO_MUSTACHE
#include "fio-stl.h"
#include
#include
typedef struct {
const char *name;
} data_s;
static void *get_var(void *ctx, fio_buf_info_s name) {
data_s *d = (data_s *)ctx;
if (name.len == 4 && !memcmp(name.buf, "name", 4))
return (void *)d->name;
return NULL;
}
static fio_buf_info_s var2str(void *var) {
char *s = (char *)var;
return FIO_BUF_INFO2(s, strlen(s));
}
int main(void) {
char template_text[] = "Hello, {{name}}!\n";
data_s data = {.name = "World"};
fio_mustache_s *m = fio_mustache_load(
.data = FIO_BUF_INFO1(template_text));
if (!m)
return 1;
char *out = fio_mustache_build(m,
.ctx = &data,
.get_var = get_var,
.var2str = var2str);
if (out) {
fwrite(out, 1, fio_bstr_len(out), stdout);
fio_bstr_free(out);
}
fio_mustache_free(m);
return 0;
}
```
---
# Crypto Core
```c
#define FIO_CRYPTO_CORE
#include "fio-stl.h"
```
The crypto slice is a small, zero-dependency toolbox for code that needs to hash, sign, encrypt, parse certificates, or derive keys without dragging in a larger library. Handy? yes. Magic security dust? no.
`FIO_CRYPTO_CORE` itself is the shared glue. It defines the AEAD function pointer types used by the symmetric ciphers and by higher-level helpers that accept “any AEAD with this call shape”. Most users include a specific module, or define `FIO_CRYPTO` to pull in the crypto set.
## Map of the Crypto Modules
| Class | Docs | What lives there |
| --- | --- | --- |
| Hash | [SHA-1](https://facil.io/edge/sha-1/), [SHA-2](https://facil.io/edge/sha-1/), [SHA-3 / SHAKE](https://facil.io/edge/sha-1/), [BLAKE2](https://facil.io/edge/blake2/) | Digest functions, streaming hash contexts, SHA/BLAKE HMAC helpers where provided. SHA-1 is legacy-only glue. |
| Symmetric | [ChaCha20-Poly1305](https://facil.io/edge/chacha20-poly1305/), [AES-GCM](https://facil.io/edge/aes-gcm/) | AEAD encryption, stream cipher helpers, and the shared in-place authenticated encryption shape. |
| Asymmetric | [Ed25519 & X25519](https://facil.io/edge/ed25519-x25519/), [P-256](https://facil.io/edge/p-256-secp256r1/), [P-384](https://facil.io/edge/p-256-secp256r1/), [RSA](https://facil.io/edge/rsa-signatures/) | Signatures, key exchange, ECIES-style X25519 encryption, and RSA signatures for TLS-style use cases. |
| PKI | [ASN.1 DER](https://facil.io/edge/asn-1-der/), [X.509](https://facil.io/edge/http-1-x-parser/), [PEM](https://facil.io/edge/p-256-secp256r1/) | DER/PEM parsing, certificate fields, hostname checks, signature checks, and certificate chain validation helpers. |
| KDF | [HKDF](https://facil.io/edge/hkdf/), [Argon2](https://facil.io/edge/argon2/), [Lyra2](https://facil.io/edge/lyra2/), [OTP](https://facil.io/edge/otp-totp/), [Secrets](https://facil.io/edge/secrets/) | Key derivation, password hashing, TOTP codes, and hashing a process secret into a stable internal value. |
| Post-Quantum | [ML-KEM-768](https://facil.io/edge/ml-entity-decoding/) | ML-KEM-768 key encapsulation and the X25519MLKEM768 hybrid key exchange shape used by TLS drafts. |
## The Shared AEAD Shape
The core header defines two function pointer types:
```c
typedef void(fio_crypto_enc_fn)(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
typedef int(fio_crypto_dec_fn)(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Current AEAD implementations that match this shape include:
- `fio_chacha20_poly1305_enc` / `fio_chacha20_poly1305_dec`
- `fio_xchacha20_poly1305_enc` / `fio_xchacha20_poly1305_dec`
- `fio_aes128_gcm_enc` / `fio_aes128_gcm_dec`
- `fio_aes256_gcm_enc` / `fio_aes256_gcm_dec`
These functions encrypt or decrypt `data` in place. `ad` is authenticated but not encrypted. `mac` is the authentication tag buffer; the current AEAD modules use 16-byte tags. Key and nonce sizes belong to the selected cipher, not to the function pointer type.
Decryption returns `0` when authentication succeeds and `-1` when it fails. Treat any failure as “message not trusted”; do not parse, log in detail, or partially use the plaintext.
## Safe Use, Plainly
This module helps when you need portable crypto building blocks inside the STL: hashing buffers, deriving session keys, signing messages, checking certificates, or doing AEAD encryption with a known protocol design.
It does **not** promise that your protocol is safe. It does not manage long-term keys for you, pick nonces, rotate secrets, maintain a root trust store, protect keys in hardware, provide FIPS validation, or substitute for an external security review. Several public-key and PKI headers explicitly note that they have not been independently audited.
Use this toolbox when:
- you need the zero-dependency facil.io STL path;
- the protocol is already designed and reviewed;
- inputs, key sizes, nonce rules, and failure paths are controlled;
- a compact embedded copy is more important than delegating to a platform stack.
Prefer audited platform/security libraries such as OpenSSL, BoringSSL, libsodium, CommonCrypto, platform TLS, or OS key stores when:
- you are building TLS, PKI, payment, identity, or compliance-sensitive systems;
- private keys live for a long time or leave the process boundary;
- certificate trust decisions must track OS/browser policy;
- side-channel hardening, hardware acceleration policy, or formal validation matters.
In short: this is a sharp little knife. Useful. Keep fingers clear.
## Implementation Notes
The crypto headers use optional CPU-specific paths where available, including SHA intrinsics, AES-NI/PCLMULQDQ, ARM crypto extensions, NEON, and AVX2. The public APIs stay the same when the implementation falls back to portable C.
The SIMD notes in `150 crypto core.h` are documentation for maintainers: ChaCha20-Poly1305 can process blocks in parallel, Curve25519 uses vectorized field add/sub/cswap where useful, and ML-KEM vectorizes NTT/reduction paths. These are implementation details, not extra guarantees.
# BLAKE2
```c
#define FIO_BLAKE2
#include "fio-stl.h"
```
BLAKE2b (64-bit, up to 64-byte digest) and BLAKE2s (32-bit, up to 32-byte digest). Both are fast, modern cryptographic hashes with built-in keyed hashing, standard HMAC wrappers, and a streaming init/consume/finalize API.
### Types
#### `fio_blake2b_s`
```c
typedef struct {
uint64_t h[8]; /* state */
uint64_t t[2]; /* total bytes processed (128-bit counter) */
uint64_t f[2]; /* finalization flags */
uint8_t buf[128]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2b_s;
```
Streaming BLAKE2b context. Treat it as opaque and initialize with `fio_blake2b_init`.
#### `fio_blake2s_s`
```c
typedef struct {
uint32_t h[8]; /* state */
uint32_t t[2]; /* total bytes processed (64-bit counter) */
uint32_t f[2]; /* finalization flags */
uint8_t buf[64]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2s_s;
```
Streaming BLAKE2s context. Treat it as opaque and initialize with `fio_blake2s_init`.
### BLAKE2b Functions
#### `fio_blake2b`
```c
FIO_IFUNC fio_u512 fio_blake2b(const void *data, uint64_t len);
```
One-shot BLAKE2b with the full 64-byte digest.
**Parameters:**
- `data` — bytes to hash.
- `len` — number of bytes.
**Returns:** the 64-byte digest as `fio_u512`.
**Note:** signature matches `fio_sha512`, so the two are interchangeable as `fio_u512 (*)(const void *, uint64_t)`.
#### `fio_blake2b_hash`
```c
SFUNC void fio_blake2b_hash(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len,
const void *restrict key,
size_t keylen);
```
Flexible-output BLAKE2b with optional keyed hashing.
**Parameters:**
- `out` — destination buffer (at least `outlen` bytes).
- `outlen` — digest length (1–64; defaults to 64 if 0).
- `data` — bytes to hash.
- `len` — data length.
- `key` — optional secret key (NULL for unkeyed).
- `keylen` — key length (0–64; clamped to 64 if larger).
**Note:** keyed BLAKE2b is a built-in MAC mode; for RFC-2104 HMAC, use `fio_blake2b_hmac`.
#### `fio_blake2b_hmac`
```c
SFUNC fio_u512 fio_blake2b_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Standard HMAC-BLAKE2b with a 64-byte digest.
**Parameters:**
- `key` — secret key.
- `key_len` — key length.
- `msg` — message to authenticate.
- `msg_len` — message length.
**Returns:** 64-byte authentication code as `fio_u512`.
#### `fio_blake2b_init`
```c
SFUNC fio_blake2b_s fio_blake2b_init(size_t outlen,
const void *key,
size_t keylen);
```
Initializes a BLAKE2b streaming context.
**Parameters:**
- `outlen` — desired digest length (1–64; defaults to 64 if 0).
- `key` — optional secret key (NULL for unkeyed).
- `keylen` — key length (0–64; clamped to 64 if larger).
#### `fio_blake2b_consume`
```c
SFUNC void fio_blake2b_consume(fio_blake2b_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds more data into a BLAKE2b streaming context. May be called repeatedly.
#### `fio_blake2b_finalize`
```c
SFUNC fio_u512 fio_blake2b_finalize(fio_blake2b_s *h);
```
Finalizes the stream and returns the digest.
**Returns:** digest as `fio_u512`. Only the first `outlen` bytes are valid.
### BLAKE2s Functions
#### `fio_blake2s`
```c
FIO_IFUNC fio_u256 fio_blake2s(const void *data, uint64_t len);
```
One-shot BLAKE2s with the full 32-byte digest.
#### `fio_blake2s_hash`
```c
SFUNC void fio_blake2s_hash(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len,
const void *restrict key,
size_t keylen);
```
Flexible-output BLAKE2s with optional keyed hashing. `outlen` is 1–32 (defaults to 32 if 0).
#### `fio_blake2s_hmac`
```c
SFUNC fio_u256 fio_blake2s_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Standard HMAC-BLAKE2s with a 32-byte digest.
#### `fio_blake2s_init`
```c
SFUNC fio_blake2s_s fio_blake2s_init(size_t outlen,
const void *key,
size_t keylen);
```
Initializes a BLAKE2s streaming context. `outlen` is 1–32 (defaults to 32 if 0).
#### `fio_blake2s_consume`
```c
SFUNC void fio_blake2s_consume(fio_blake2s_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds more data into a BLAKE2s streaming context.
#### `fio_blake2s_finalize`
```c
SFUNC fio_u256 fio_blake2s_finalize(fio_blake2s_s *h);
```
Finalizes the stream and returns the digest.
### Examples
```c
#define FIO_BLAKE2
#include "fio-stl.h"
void examples(void) {
const char *msg = "Hello, BLAKE2!";
/* One-shot */
fio_u512 b2b = fio_blake2b(msg, strlen(msg));
fio_u256 b2s = fio_blake2s(msg, strlen(msg));
/* Flexible output with key */
uint8_t digest[16];
fio_blake2b_hash(digest, 16, msg, strlen(msg), "key", 3);
/* Streaming */
fio_blake2b_s ctx = fio_blake2b_init(64, NULL, 0);
fio_blake2b_consume(&ctx, "Hello, ", 7);
fio_blake2b_consume(&ctx, "BLAKE2!", 7);
fio_u512 result = fio_blake2b_finalize(&ctx);
/* HMAC */
fio_u512 mac = fio_blake2b_hmac("key", 3, msg, strlen(msg));
}
```
### BLAKE2b vs BLAKE2s
| Property | BLAKE2b | BLAKE2s |
|---|---|---|
| Word size | 64-bit | 32-bit |
| Block size | 128 bytes | 64 bytes |
| Max digest | 64 bytes | 32 bytes |
| Max key | 64 bytes | 32 bytes |
| Rounds | 12 | 10 |
| Result type | `fio_u512` | `fio_u256` |
------------------------------------------------------------
# ChaCha20 & Poly1305
```c
#define FIO_CHACHA
#include "fio-stl.h"
```
ChaCha20 stream cipher, Poly1305 authenticator, and the ChaCha20-Poly1305 AEAD combination. Also includes XChaCha20-Poly1305 with a 192-bit nonce, which is safer for random nonces than the 96-bit variant.
**Security note:** this implementation has not been independently audited. Use at your own risk, and prefer a tested cryptographic library when one is available.
### ChaCha20-Poly1305 API
#### `fio_chacha20_poly1305_enc`
```c
SFUNC void fio_chacha20_poly1305_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
In-place encryption with a 16-byte Poly1305 tag.
**Parameters:**
- `mac` — output buffer; must have at least 16 writable bytes.
- `data` — plaintext buffer; encrypted in place.
- `len` — data length.
- `ad` — additional authenticated data (not encrypted); may be `NULL`.
- `adlen` — length of `ad`.
- `key` — 32-byte key.
- `nonce` — 12-byte nonce.
#### `fio_chacha20_poly1305_dec`
```c
SFUNC int fio_chacha20_poly1305_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
In-place decryption with tag verification.
**Returns:** `0` on success, `-1` if authentication fails. On failure `data` is zeroed.
#### `fio_chacha20_poly1305_auth`
```c
SFUNC void fio_chacha20_poly1305_auth(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Computes the Poly1305 tag for already-encrypted data without decrypting it.
### Standalone ChaCha20 / Poly1305
#### `fio_chacha20`
```c
SFUNC void fio_chacha20(void *restrict data,
size_t len,
const void *key,
const void *nonce,
uint32_t counter);
```
In-place ChaCha20 encryption or decryption.
**Parameters:**
- `data` — buffer to transform in place.
- `len` — buffer length.
- `key` — 32-byte key.
- `nonce` — 12-byte nonce.
- `counter` — block counter; usually `1` unless resuming mid-ciphertext.
#### `fio_poly1305_auth`
```c
SFUNC void fio_poly1305_auth(void *restrict mac_dest,
void *restrict message,
size_t len,
const void *ad,
size_t ad_len,
const void *key256bits);
```
Computes a Poly1305 MAC for `message` and `ad` using a 32-byte key.
**Parameters:**
- `mac_dest` — output buffer; must have at least 16 writable bytes.
- `message` — message to authenticate; may be `NULL` if `len` is 0.
- `len` — message length.
- `ad` — additional data; may be `NULL`.
- `ad_len` — additional data length.
- `key256bits` — 32-byte Poly1305 key.
### XChaCha20-Poly1305 API
XChaCha20 uses a 24-byte nonce, making random nonces safe from birthday-bound collisions.
#### `fio_xchacha20_poly1305_enc`
```c
SFUNC void fio_xchacha20_poly1305_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same shape as `fio_chacha20_poly1305_enc`, but `nonce` must be 24 bytes.
#### `fio_xchacha20_poly1305_dec`
```c
SFUNC int fio_xchacha20_poly1305_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same shape as `fio_chacha20_poly1305_dec`, but `nonce` must be 24 bytes.
#### `fio_xchacha20`
```c
SFUNC void fio_xchacha20(void *restrict data,
size_t len,
const void *key,
const void *nonce,
uint32_t counter);
```
In-place XChaCha20 encryption or decryption.
### Example
```c
#define FIO_CHACHA
#include "fio-stl.h"
int main(void) {
uint8_t key[32] = {0};
uint8_t nonce[12] = {0};
uint8_t msg[32] = "hello, chacha world!";
uint8_t tag[16];
fio_rand_bytes_secure(key, sizeof(key));
fio_rand_bytes_secure(nonce, sizeof(nonce));
fio_chacha20_poly1305_enc(tag, msg, sizeof(msg), NULL, 0, key, nonce);
if (fio_chacha20_poly1305_dec(tag, msg, sizeof(msg), NULL, 0, key, nonce)) {
printf("authentication failed\n");
return 1;
}
return 0;
}
```
------------------------------------------------------------
# HKDF
```c
#define FIO_HKDF
#include "fio-stl.h"
```
HMAC-based Key Derivation Function per [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869). HKDF extracts entropy from input keying material into a pseudorandom key, then expands that key into one or more output keys.
Supports SHA-256 and SHA-384 as the underlying HMAC.
**Note:** HKDF needs `fio_sha256_hmac` and `fio_sha512_hmac`. Define `FIO_SHA2` before `FIO_HKDF`, or use `FIO_CRYPTO` to pull in everything.
### Constants
#### `FIO_HKDF_SHA256_HASH_LEN`
```c
#define FIO_HKDF_SHA256_HASH_LEN 32
```
SHA-256 hash length in bytes.
#### `FIO_HKDF_SHA384_HASH_LEN`
```c
#define FIO_HKDF_SHA384_HASH_LEN 48
```
SHA-384 hash length in bytes.
### Functions
#### `fio_hkdf_extract`
```c
SFUNC void fio_hkdf_extract(void *restrict prk,
const void *restrict salt,
size_t salt_len,
const void *restrict ikm,
size_t ikm_len,
int use_sha384);
```
HKDF-Extract: `PRK = HMAC-Hash(salt, IKM)`.
**Parameters:**
- `prk` — output buffer (32 bytes for SHA-256, 48 for SHA-384).
- `salt` — optional salt; if `NULL`, a zero string of hash length is used.
- `salt_len` — salt length.
- `ikm` — input keying material.
- `ikm_len` — IKM length.
- `use_sha384` — non-zero for SHA-384, zero for SHA-256.
#### `fio_hkdf_expand`
```c
SFUNC void fio_hkdf_expand(void *restrict okm,
size_t okm_len,
const void *restrict prk,
size_t prk_len,
const void *restrict info,
size_t info_len,
int use_sha384);
```
HKDF-Expand: `OKM = HKDF-Expand(PRK, info, L)`.
**Parameters:**
- `okm` — output buffer.
- `okm_len` — desired output length (max `255 * hash_len`).
- `prk` — pseudorandom key from Extract.
- `prk_len` — 32 for SHA-256, 48 for SHA-384.
- `info` — optional context/info string; may be `NULL`.
- `info_len` — info length.
- `use_sha384` — non-zero for SHA-384, zero for SHA-256.
#### `fio_hkdf`
```c
SFUNC void fio_hkdf(void *restrict okm,
size_t okm_len,
const void *restrict salt,
size_t salt_len,
const void *restrict ikm,
size_t ikm_len,
const void *restrict info,
size_t info_len,
int use_sha384);
```
Combined HKDF: Extract followed by Expand in one call.
### Example
```c
#define FIO_SHA2
#define FIO_HKDF
#include "fio-stl.h"
int main(void) {
const char *ikm = "input keying material";
const char *salt = "salt";
const char *info = "app context";
uint8_t key[32];
fio_hkdf(key, sizeof(key), salt, strlen(salt),
ikm, strlen(ikm), info, strlen(info), 0);
return 0;
}
```
------------------------------------------------------------
# SHA-1
```c
#define FIO_SHA1
#include "fio-stl.h"
```
SHA-1 hashing and HMAC. It is cryptographically broken, so do not use it for anything that needs security. Some protocols (hello, WebSockets) still ask for it, so it lives here for compatibility.
### Types
#### `fio_sha1_s`
```c
typedef union {
#ifdef __SIZEOF_INT128__
__uint128_t align__;
#else
uint64_t align__;
#endif
uint32_t v[5];
uint8_t digest[20];
} fio_sha1_s;
```
Container for the 20-byte SHA-1 digest.
**Members:**
- `align__` - alignment padding.
- `v` - the 5 × 32-bit state words.
- `digest` - the 20-byte digest as raw bytes.
### API Functions
#### `fio_sha1`
```c
fio_sha1_s fio_sha1(const void *data, uint64_t len);
```
One-shot SHA-1 hash of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_sha1_s` containing the 20-byte digest.
#### `fio_sha1_hmac`
```c
fio_sha1_s fio_sha1_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA1, producing a 20-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_sha1_s` containing the 20-byte HMAC.
**Note:** keys longer than 64 bytes are first hashed with SHA-1.
#### `fio_sha1_len`
```c
size_t fio_sha1_len(void);
```
**Returns:** the SHA-1 digest length in bytes (20).
#### `fio_sha1_digest`
```c
uint8_t *fio_sha1_digest(fio_sha1_s *s);
```
**Parameters:**
- `s` - pointer to a `fio_sha1_s` result.
**Returns:** a pointer to the 20-byte digest inside `s`.
### Examples
#### One-shot hash
```c
#define FIO_SHA1
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
fio_sha1_s r = fio_sha1(msg, strlen(msg));
for (size_t i = 0; i < fio_sha1_len(); ++i)
printf("%02x", fio_sha1_digest(&r)[i]);
printf("\n");
return 0;
}
```
#### HMAC
```c
fio_sha1_s mac = fio_sha1_hmac("secret", 6, "hello", 5);
```
**Note:** SHA-1 is broken. Prefer SHA-256, SHA-3, or another modern hash for security-sensitive work.
------------------------------------------------------------
# SHA-2
```c
#define FIO_SHA2
#include "fio-stl.h"
```
SHA-256 and SHA-512 hashing, with both one-shot and streaming APIs plus HMAC. These are the workhorse hashes for checksums, signatures, and anything that needs a collision-resistant digest.
### Types
#### `fio_sha256_s`
```c
typedef struct {
fio_u256 hash;
fio_u512 cache;
uint64_t total_len;
} fio_sha256_s;
```
Streaming SHA-256 state. Initialize with `fio_sha256_init`, feed with `fio_sha256_consume`, and finish with `fio_sha256_finalize`.
#### `fio_sha512_s`
```c
typedef struct {
fio_u512 hash;
fio_u1024 cache;
uint64_t total_len;
} fio_sha512_s;
```
Streaming SHA-512 state. Initialize with `fio_sha512_init`, feed with `fio_sha512_consume`, and finish with `fio_sha512_finalize`.
### SHA-256 Functions
#### `fio_sha256`
```c
fio_u256 fio_sha256(const void *data, uint64_t len);
```
One-shot SHA-256 of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_u256` containing the 32-byte digest.
#### `fio_sha256_init`
```c
fio_sha256_s fio_sha256_init(void);
```
**Returns:** a freshly initialized SHA-256 streaming state.
#### `fio_sha256_consume`
```c
void fio_sha256_consume(fio_sha256_s *h, const void *data, uint64_t len);
```
Feeds more data into a streaming SHA-256 hash. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming state.
- `data` - pointer to the data to add.
- `len` - length of the data in bytes.
#### `fio_sha256_finalize`
```c
fio_u256 fio_sha256_finalize(fio_sha256_s *h);
```
Finalizes the hash and returns the 32-byte digest.
**Parameters:**
- `h` - pointer to the streaming state.
**Returns:** a `fio_u256` containing the digest.
**Note:** after finalization, `h` should not be reused without re-initialization.
### SHA-512 Functions
#### `fio_sha512`
```c
fio_u512 fio_sha512(const void *data, uint64_t len);
```
One-shot SHA-512 of `len` bytes at `data`.
**Parameters:**
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
**Returns:** a `fio_u512` containing the 64-byte digest.
#### `fio_sha512_init`
```c
fio_sha512_s fio_sha512_init(void);
```
**Returns:** a freshly initialized SHA-512 streaming state.
#### `fio_sha512_consume`
```c
void fio_sha512_consume(fio_sha512_s *h, const void *data, uint64_t len);
```
Feeds more data into a streaming SHA-512 hash. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming state.
- `data` - pointer to the data to add.
- `len` - length of the data in bytes.
#### `fio_sha512_finalize`
```c
fio_u512 fio_sha512_finalize(fio_sha512_s *h);
```
Finalizes the hash and returns the 64-byte digest.
**Parameters:**
- `h` - pointer to the streaming state.
**Returns:** a `fio_u512` containing the digest.
**Note:** after finalization, `h` should not be reused without re-initialization.
### SHA-384 Functions
SHA-384 is SHA-512 with different initial values, truncated to 48 bytes
(truncating a SHA-512 *result* is NOT the same as SHA-384).
#### `fio_sha384`
```c
fio_u512 fio_sha384(const void *data, uint64_t len);
```
Computes SHA-384 in a single call.
**Returns:** a `fio_u512` whose first 48 bytes hold the digest.
#### `fio_sha384_init` / `fio_sha384_consume` / `fio_sha384_finalize`
```c
fio_sha512_s fio_sha384_init(void);
void fio_sha384_consume(fio_sha512_s *h, const void *data, uint64_t len);
fio_u512 fio_sha384_finalize(fio_sha512_s *h);
```
Streaming SHA-384: same state type as SHA-512; `fio_sha384_init` seeds the
SHA-384 initial values, consume/finalize alias the SHA-512 operations.
### HMAC Functions
#### `fio_sha256_hmac`
```c
fio_u256 fio_sha256_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA256, producing a 32-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_u256` containing the 32-byte HMAC.
**Note:** keys longer than 64 bytes are hashed first.
#### `fio_sha512_hmac`
```c
fio_u512 fio_sha512_hmac(const void *key,
uint64_t key_len,
const void *msg,
uint64_t msg_len);
```
Computes HMAC-SHA512, producing a 64-byte authentication code.
**Parameters:**
- `key` - pointer to the secret key.
- `key_len` - length of the key in bytes.
- `msg` - pointer to the message to authenticate.
- `msg_len` - length of the message in bytes.
**Returns:** a `fio_u512` containing the 64-byte HMAC.
**Note:** keys longer than 128 bytes are hashed first.
### Examples
#### One-shot SHA-256
```c
#define FIO_SHA2
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
fio_u256 r = fio_sha256(msg, strlen(msg));
for (size_t i = 0; i < sizeof(r.u8); ++i)
printf("%02x", r.u8[i]);
printf("\n");
return 0;
}
```
#### Streaming SHA-256
```c
fio_sha256_s h = fio_sha256_init();
fio_sha256_consume(&h, "hello, ", 7);
fio_sha256_consume(&h, "world", 5);
fio_u256 r = fio_sha256_finalize(&h);
```
#### HMAC-SHA256
```c
fio_u256 mac = fio_sha256_hmac("secret", 6, "hello", 5);
```
**Note:** all state lives in caller-owned variables. The functions do not allocate heap memory and are thread-safe as long as each thread uses its own state.
------------------------------------------------------------
# SHA-3
```c
#define FIO_SHA3
#include "fio-stl.h"
```
The SHA-3 family (SHA3-224/256/384/512) and the SHAKE128/SHAKE256 extendable-output functions, built on Keccak-f[1600]. All variants share one streaming context type.
### Types
#### `fio_sha3_s`
```c
typedef struct {
uint64_t state[25];
uint8_t buf[200];
size_t buflen;
size_t rate;
size_t outlen;
uint8_t delim;
} fio_sha3_s;
```
Streaming context for every SHA-3 and SHAKE variant.
**Members:**
- `state` - the 25-word Keccak-f[1600] state.
- `buf` - partial-block input buffer.
- `buflen` - bytes currently buffered.
- `rate` - absorption/squeezing rate in bytes, set by the init function.
- `outlen` - fixed output length in bytes for SHA-3; 0 for SHAKE.
- `delim` - domain separator (`0x06` for SHA-3, `0x1F` for SHAKE).
**Note:** treat this as opaque. Always initialize it with one of the `fio_sha3_*_init` or `fio_shake*_init` functions.
### SHA-3 Fixed-Output Functions
#### `fio_sha3_224_init`
```c
fio_sha3_s fio_sha3_224_init(void);
```
**Returns:** a SHA3-224 context (28-byte output, rate 144).
#### `fio_sha3_256_init`
```c
fio_sha3_s fio_sha3_256_init(void);
```
**Returns:** a SHA3-256 context (32-byte output, rate 136).
#### `fio_sha3_384_init`
```c
fio_sha3_s fio_sha3_384_init(void);
```
**Returns:** a SHA3-384 context (48-byte output, rate 104).
#### `fio_sha3_512_init`
```c
fio_sha3_s fio_sha3_512_init(void);
```
**Returns:** a SHA3-512 context (64-byte output, rate 72).
#### `fio_sha3_consume`
```c
void fio_sha3_consume(fio_sha3_s *restrict h,
const void *restrict data,
size_t len);
```
Feeds `len` bytes into a SHA-3 context. May be called repeatedly.
**Parameters:**
- `h` - pointer to the streaming context.
- `data` - pointer to the data to absorb.
- `len` - length of the data in bytes.
#### `fio_sha3_finalize`
```c
void fio_sha3_finalize(fio_sha3_s *restrict h, void *restrict out);
```
Pads, permutes, and writes `h->outlen` digest bytes to `out`.
**Parameters:**
- `h` - pointer to the streaming context.
- `out` - output buffer, must hold at least `h->outlen` bytes.
**Note:** do not call this on a SHAKE context. Use `fio_shake_squeeze` instead.
#### `fio_sha3_224`
```c
void fio_sha3_224(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-224.
**Parameters:**
- `out` - output buffer (28 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_256`
```c
void fio_sha3_256(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-256.
**Parameters:**
- `out` - output buffer (32 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_384`
```c
void fio_sha3_384(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-384.
**Parameters:**
- `out` - output buffer (48 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_sha3_512`
```c
void fio_sha3_512(void *restrict out,
const void *restrict data,
size_t len);
```
One-shot SHA3-512.
**Parameters:**
- `out` - output buffer (64 bytes).
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
### SHAKE Extendable-Output Functions
#### `fio_shake128_init`
```c
fio_sha3_s fio_shake128_init(void);
```
**Returns:** a SHAKE128 context (variable output, rate 168).
#### `fio_shake256_init`
```c
fio_sha3_s fio_shake256_init(void);
```
**Returns:** a SHAKE256 context (variable output, rate 136).
#### `fio_shake_consume`
```c
#define fio_shake_consume fio_sha3_consume
```
Alias for `fio_sha3_consume`. Feeds data into a SHAKE context.
#### `fio_shake_squeeze`
```c
void fio_shake_squeeze(fio_sha3_s *restrict h,
void *restrict out,
size_t outlen);
```
Squeezes `outlen` bytes from a SHAKE context. On the first call it applies padding and runs the first permutation; later calls continue squeezing and re-permute as needed.
**Parameters:**
- `h` - pointer to the SHAKE context.
- `out` - output buffer, must hold at least `outlen` bytes.
- `outlen` - number of bytes to squeeze.
#### `fio_shake128`
```c
void fio_shake128(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len);
```
One-shot SHAKE128.
**Parameters:**
- `out` - output buffer.
- `outlen` - number of bytes to produce.
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
#### `fio_shake256`
```c
void fio_shake256(void *restrict out,
size_t outlen,
const void *restrict data,
size_t len);
```
One-shot SHAKE256.
**Parameters:**
- `out` - output buffer.
- `outlen` - number of bytes to produce.
- `data` - pointer to the data to hash.
- `len` - length of the data in bytes.
### Examples
#### One-shot SHA3-256
```c
#define FIO_SHA3
#include "fio-stl.h"
#include
#include
int main(void) {
const char *msg = "hello";
uint8_t digest[32];
fio_sha3_256(digest, msg, strlen(msg));
for (size_t i = 0; i < sizeof(digest); ++i)
printf("%02x", digest[i]);
printf("\n");
return 0;
}
```
#### Streaming SHA3-512
```c
fio_sha3_s h = fio_sha3_512_init();
fio_sha3_consume(&h, "hello, ", 7);
fio_sha3_consume(&h, "world", 5);
uint8_t digest[64];
fio_sha3_finalize(&h, digest);
```
#### SHAKE256 extendable output
```c
const char *seed = "hello";
uint8_t out[128];
fio_sha3_s h = fio_shake256_init();
fio_shake_consume(&h, seed, strlen(seed));
fio_shake_squeeze(&h, out, sizeof(out));
```
**Note:** state is caller-owned; no heap allocation occurs. Keep SHA-3 and SHAKE contexts separate at finalization time (`fio_sha3_finalize` vs. `fio_shake_squeeze`).
------------------------------------------------------------
# AES-GCM
```c
#define FIO_AES
#include "fio-stl.h"
```
Authenticated encryption with associated data (AEAD) using AES-128-GCM and AES-256-GCM. The API is in-place and matches [`fio_chacha20_poly1305`](https://facil.io/edge/chacha20-poly1305/), so the two cipher suites can be swapped with the same call shape.
Hardware acceleration is used when available (x86 AES-NI + PCLMULQDQ, ARM crypto extensions), with a portable fallback otherwise.
**Security note:** GCM nonces must never be reused with the same key. A 96-bit nonce runs out of uniqueness long before the key does; generate a fresh nonce for every message.
### API Functions
#### `fio_aes128_gcm_enc`
```c
SFUNC void fio_aes128_gcm_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Encrypts `data` in place with AES-128-GCM and writes a 16-byte authentication tag to `mac`.
**Parameters:**
- `mac` — output buffer; must have at least 16 writable bytes.
- `data` — plaintext buffer; encrypted in place. May be `NULL` if `len` is 0.
- `len` — plaintext length in bytes.
- `ad` — additional authenticated data; not encrypted but covered by the tag. May be `NULL`.
- `adlen` — length of `ad`.
- `key` — 16-byte AES-128 key.
- `nonce` — 12-byte nonce.
#### `fio_aes128_gcm_dec`
```c
SFUNC int fio_aes128_gcm_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Decrypts `data` in place and verifies the 16-byte tag in `mac`.
**Parameters:** same as `fio_aes128_gcm_enc`.
**Returns:** `0` on success, `-1` if authentication fails. On failure `data` is left unchanged.
#### `fio_aes256_gcm_enc`
```c
SFUNC void fio_aes256_gcm_enc(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same as `fio_aes128_gcm_enc` but uses a 32-byte AES-256 key.
#### `fio_aes256_gcm_dec`
```c
SFUNC int fio_aes256_gcm_dec(void *restrict mac,
void *restrict data,
size_t len,
const void *ad,
size_t adlen,
const void *key,
const void *nonce);
```
Same as `fio_aes128_gcm_dec` but uses a 32-byte AES-256 key.
### Example
```c
#define FIO_AES
#include "fio-stl.h"
int main(void) {
uint8_t key[32] = {0};
uint8_t nonce[12] = {0};
uint8_t msg[32] = "hello, gcm world!";
uint8_t tag[16];
fio_rand_bytes_secure(key, sizeof(key));
fio_rand_bytes_secure(nonce, sizeof(nonce));
fio_aes256_gcm_enc(tag, msg, sizeof(msg), NULL, 0, key, nonce);
/* msg is now ciphertext; tag authenticates it. */
if (fio_aes256_gcm_dec(tag, msg, sizeof(msg), NULL, 0, key, nonce)) {
printf("authentication failed\n");
return 1;
}
/* msg is restored plaintext. */
return 0;
}
```
------------------------------------------------------------
# Ed25519 & X25519
```c
#define FIO_ED25519
#include "fio-stl.h"
```
Elliptic-curve cryptography over Curve25519: Ed25519 for signatures and X25519 for key exchange. Both provide 128-bit security with compact 32-byte keys.
**Security note:** this implementation has not been independently audited. Use at your own risk, and prefer a tested cryptographic library when available.
### Ed25519 Digital Signatures
- Secret key: 32 bytes
- Public key: 32 bytes
- Signature: 64 bytes
#### `fio_ed25519_keypair`
```c
SFUNC void fio_ed25519_keypair(uint8_t secret_key[32], uint8_t public_key[32]);
```
Generates a new random Ed25519 key pair.
#### `fio_ed25519_public_key`
```c
SFUNC void fio_ed25519_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
```
Derives the public key from a secret key.
#### `fio_ed25519_sign`
```c
SFUNC int fio_ed25519_sign(uint8_t signature[64],
const void *message,
size_t len,
const uint8_t secret_key[32],
const uint8_t public_key[32]);
```
Signs a message. Returns `0` on success, `-1` on failure. The signature is deterministic.
#### `fio_ed25519_verify`
```c
SFUNC int fio_ed25519_verify(const uint8_t signature[64],
const void *message,
size_t len,
const uint8_t public_key[32]);
```
Verifies a signature. Returns `0` on success, `-1` on failure.
### X25519 Key Exchange
- Secret key: 32 bytes
- Public key: 32 bytes
- Shared secret: 32 bytes
#### `fio_x25519_keypair`
```c
SFUNC void fio_x25519_keypair(uint8_t secret_key[32], uint8_t public_key[32]);
```
Generates a new random X25519 key pair.
#### `fio_x25519_public_key`
```c
SFUNC void fio_x25519_public_key(uint8_t public_key[32],
const uint8_t secret_key[32]);
```
Derives the public key from a secret key.
#### `fio_x25519_shared_secret`
```c
SFUNC int fio_x25519_shared_secret(uint8_t shared_secret[32],
const uint8_t secret_key[32],
const uint8_t their_public_key[32]);
```
Computes a shared secret. Returns `0` on success, `-1` if the result is the all-zero point.
**Note:** pass the shared secret through a KDF such as HKDF before using it as an encryption key.
### Key Conversion
#### `fio_ed25519_sk_to_x25519`
```c
SFUNC void fio_ed25519_sk_to_x25519(uint8_t x_secret_key[32],
const uint8_t ed_secret_key[32]);
```
Converts an Ed25519 secret key to an X25519 secret key.
#### `fio_ed25519_pk_to_x25519`
```c
SFUNC void fio_ed25519_pk_to_x25519(uint8_t x_public_key[32],
const uint8_t ed_public_key[32]);
```
Converts an Ed25519 public key to an X25519 public key.
### ECIES Public-Key Encryption
#### `FIO_X25519_CIPHERTEXT_LEN`
```c
#define FIO_X25519_CIPHERTEXT_LEN(message_len) ((message_len) + 48)
```
Plaintext plus 32-byte ephemeral public key and 16-byte MAC.
#### `FIO_X25519_PLAINTEXT_LEN`
```c
#define FIO_X25519_PLAINTEXT_LEN(ciphertext_len) \
((ciphertext_len) > 48 ? ((ciphertext_len)-48) : 0)
```
Reverses `FIO_X25519_CIPHERTEXT_LEN`. Returns `0` for invalid ciphertext.
#### `fio_x25519_encrypt`
```c
SFUNC int fio_x25519_encrypt(uint8_t *ciphertext,
const void *message,
size_t message_len,
fio_crypto_enc_fn encryption_function,
const uint8_t recipient_pk[32]);
```
Encrypts a message to an X25519 public key. Ciphertext format: `ephemeral_pk || mac || encrypted_data`.
**Parameters:**
- `ciphertext` — output buffer (`message_len + 48` bytes).
- `message` — plaintext.
- `message_len` — plaintext length.
- `encryption_function` — e.g., `fio_chacha20_poly1305_enc`.
- `recipient_pk` — recipient's 32-byte X25519 public key.
**Returns:** `0` on success, `-1` on failure.
#### `fio_x25519_decrypt`
```c
SFUNC int fio_x25519_decrypt(uint8_t *plaintext,
const uint8_t *ciphertext,
size_t ciphertext_len,
fio_crypto_dec_fn decryption_function,
const uint8_t recipient_sk[32]);
```
Decrypts a message using the recipient's X25519 secret key.
**Parameters:**
- `plaintext` — output buffer (`ciphertext_len - 48` bytes).
- `ciphertext` — ciphertext from `fio_x25519_encrypt`.
- `ciphertext_len` — total ciphertext length (must be at least 48).
- `decryption_function` — e.g., `fio_chacha20_poly1305_dec`.
- `recipient_sk` — recipient's 32-byte X25519 secret key.
**Returns:** `0` on success, `-1` on failure.
### Example
```c
#define FIO_ED25519
#include "fio-stl.h"
int main(void) {
uint8_t sk[32], pk[32], sig[64];
fio_ed25519_keypair(sk, pk);
const char *msg = "hello, ed25519";
fio_ed25519_sign(sig, msg, strlen(msg), sk, pk);
if (fio_ed25519_verify(sig, msg, strlen(msg), pk)) {
printf("bad signature\n");
return 1;
}
return 0;
}
```
------------------------------------------------------------
# P-256 (secp256r1)
```c
#define FIO_P256
#include "fio-stl.h"
```
P-256 is the NIST `secp256r1` curve used by TLS and certificate tooling. This module covers two jobs: ECDSA signatures over SHA-256 message hashes, and ECDH shared-secret creation.
**Security note:** this implementation has not been independently audited. Use it when the zero-dependency STL path matters; prefer a tested crypto library for long-lived keys, compliance, or high-value trust decisions.
## What It Provides
| Use | Function | Notes |
| --- | --- | --- |
| Sign | `fio_ecdsa_p256_sign` | Creates a DER-encoded ECDSA signature from a 32-byte SHA-256 hash. |
| Verify DER signature | `fio_ecdsa_p256_verify` | Accepts `SEQUENCE { r INTEGER, s INTEGER }` and a 65-byte uncompressed public key. |
| Verify raw signature | `fio_ecdsa_p256_verify_raw` | Accepts raw 32-byte `r`, `s`, `x`, and `y` values. |
| Key pair | `fio_p256_keypair` | Produces a 32-byte secret key and a 65-byte uncompressed public key. |
| Shared secret | `fio_p256_shared_secret` | Accepts compressed or uncompressed peer public keys and returns the x-coordinate. |
The curve parameters are the standard P-256 parameters from NIST FIPS 186-4:
- `p = 2^256 - 2^224 + 2^192 + 2^96 - 1`
- `n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551`
- curve equation: `y² = x³ - 3x + b (mod p)`
## ECDSA Signatures
### `fio_ecdsa_p256_sign`
```c
SFUNC int fio_ecdsa_p256_sign(uint8_t *sig,
size_t *sig_len,
size_t sig_capacity,
const uint8_t msg_hash[32],
const uint8_t secret_key[32]);
```
Signs a 32-byte SHA-256 message hash. The function writes a DER-encoded signature to `sig` and stores the actual length in `sig_len`.
- `sig_capacity` must be at least `72` bytes.
- `secret_key` is a 32-byte big-endian scalar and must be in the valid range `1..n-1`.
- The function generates a random ECDSA nonce internally and retries invalid nonces.
- Returns `0` on success, `-1` on invalid input or signing failure.
It signs the hash you give it; it does not hash the original message. No sneaky kitchen work here.
### `fio_ecdsa_p256_verify`
```c
SFUNC int fio_ecdsa_p256_verify(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
const uint8_t *pubkey,
size_t pubkey_len);
```
Verifies a DER-encoded ECDSA signature against a 32-byte SHA-256 hash.
- `sig` is a DER `SEQUENCE { r INTEGER, s INTEGER }`.
- `msg_hash` is exactly 32 bytes.
- `pubkey` must be an uncompressed P-256 public key: `0x04 || x || y`.
- `pubkey_len` must be `65`.
- Returns `0` for a valid signature, `-1` for invalid input or a bad signature.
### `fio_ecdsa_p256_verify_raw`
```c
SFUNC int fio_ecdsa_p256_verify_raw(const uint8_t r[32],
const uint8_t s[32],
const uint8_t msg_hash[32],
const uint8_t pubkey_x[32],
const uint8_t pubkey_y[32]);
```
Verifies a signature when `r` and `s` are already decoded. Public-key coordinates are 32-byte big-endian values.
The verifier checks that `r` and `s` are in range, checks the public point is on the P-256 curve, performs the ECDSA verification equation, and returns `0` only when the signature matches.
## ECDH Key Exchange
### `fio_p256_keypair`
```c
SFUNC int fio_p256_keypair(uint8_t secret_key[32], uint8_t public_key[65]);
```
Generates a P-256 key pair.
- `secret_key` receives a 32-byte scalar.
- `public_key` receives `0x04 || x || y` (65 bytes).
- Returns `0` on success, `-1` on bad arguments or random generation failure.
### `fio_p256_shared_secret`
```c
SFUNC int fio_p256_shared_secret(uint8_t shared_secret[32],
const uint8_t secret_key[32],
const uint8_t *their_public_key,
size_t their_public_key_len);
```
Computes a P-256 ECDH shared secret using your secret key and their public key.
- `secret_key` is a 32-byte scalar in the valid range `1..n-1`.
- `their_public_key` may be uncompressed (`65` bytes, `0x04 || x || y`) or compressed (`33` bytes, `0x02/0x03 || x`).
- The peer point is decompressed when needed and checked against the curve.
- `shared_secret` receives the 32-byte x-coordinate of the result.
- Returns `0` on success, `-1` on invalid input, invalid point, point at infinity, or all-zero result.
Run the shared secret through a KDF such as HKDF before using it as an encryption key. Raw ECDH output is an ingredient, not dinner.
## Example
```c
#define FIO_P256
#define FIO_SHA2
#include "fio-stl.h"
int main(void) {
uint8_t secret_key[32];
uint8_t public_key[65];
if (fio_p256_keypair(secret_key, public_key))
return 1;
const char *message = "hello, p-256";
fio_u256 hash = fio_sha256(message, strlen(message));
uint8_t sig[72];
size_t sig_len = 0;
if (fio_ecdsa_p256_sign(sig, &sig_len, sizeof(sig), hash.u8, secret_key))
return 1;
if (fio_ecdsa_p256_verify(sig, sig_len, hash.u8, public_key, 65))
return 1;
return 0;
}
```
------------------------------------------------------------
# P-384 (secp384r1)
```c
#define FIO_P384
#include "fio-stl.h"
```
P-384 is the NIST `secp384r1` curve. In this STL, it is a verifier: it checks ECDSA P-384 signatures, mainly for TLS 1.3 certificate chains and CA roots that use P-384.
There is no signing or key-generation API here. Just verification. Nice and focused.
**Security note:** this implementation has not been independently audited. It is meant for verification of public data; prefer a tested crypto library when certificate trust is central to your application.
## What It Provides
| Use | Function | Notes |
| --- | --- | --- |
| Verify DER signature | `fio_ecdsa_p384_verify` | Accepts `SEQUENCE { r INTEGER, s INTEGER }` and a 97-byte uncompressed public key. |
| Verify raw signature | `fio_ecdsa_p384_verify_raw` | Accepts raw 48-byte `r`, `s`, `x`, and `y` values. |
Curve parameters from NIST FIPS 186-4:
- `p = 2^384 - 2^128 - 2^96 + 2^32 - 1`
- `n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973`
- curve equation: `y² = x³ - 3x + b (mod p)`
- coordinate size: 48 bytes
## Verification API
### `fio_ecdsa_p384_verify`
```c
SFUNC int fio_ecdsa_p384_verify(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
const uint8_t *pubkey,
size_t pubkey_len);
```
Verifies a DER-encoded ECDSA P-384 signature over a 48-byte SHA-384 hash.
- `sig` is a DER `SEQUENCE { r INTEGER, s INTEGER }`.
- `sig_len` is the signature length in bytes.
- `msg_hash` is the SHA-384 hash of the message, exactly 48 bytes.
- `pubkey` must be an uncompressed public key: `0x04 || x || y`.
- `pubkey_len` must be `97`.
- Returns `0` for a valid signature, `-1` for invalid input or a bad signature.
The function validates the public-key wrapper, decodes `r` and `s` from DER, then calls `fio_ecdsa_p384_verify_raw`.
Compressed public keys are not accepted by this API.
### `fio_ecdsa_p384_verify_raw`
```c
SFUNC int fio_ecdsa_p384_verify_raw(const uint8_t r[48],
const uint8_t s[48],
const uint8_t msg_hash[48],
const uint8_t pubkey_x[48],
const uint8_t pubkey_y[48]);
```
Verifies an ECDSA P-384 signature when the signature and public key are already decoded.
All inputs are fixed-width big-endian values:
- `r` and `s`: 48-byte signature scalars.
- `msg_hash`: 48-byte SHA-384 hash.
- `pubkey_x` and `pubkey_y`: 48-byte public-key coordinates.
The verifier checks that `r` and `s` are in range, checks the public point is on the P-384 curve, computes the ECDSA verification equation, and returns `0` only when `r == R.x mod n`.
## Example
```c
#define FIO_P384
#include "fio-stl.h"
int verify_cert_sig(const uint8_t *sig,
size_t sig_len,
const uint8_t hash[48],
const uint8_t pubkey[97]) {
if (fio_ecdsa_p384_verify(sig, sig_len, hash, pubkey, 97))
return -1;
return 0;
}
```
## Implementation Notes
The implementation uses 6 × 64-bit limbs for field elements, Jacobian coordinates for point work, P-384-specific modular reduction, and a double-scalar multiplication path for ECDSA verification.
The scalar multiplication is for public verification data. Do not treat this header as a P-384 signing toolkit; there is no public signing API, and private-key use should go to an audited library.
------------------------------------------------------------
# ASN.1 DER
```c
#define FIO_DER
#include "fio-stl.h"
```
ASN.1 DER is the small binary grammar hiding inside X.509 certificates, keys, signatures, and TLS messages. This module parses and writes the pieces facil.io needs for that world.
The parser is non-allocating. Parsed elements point into the original DER buffer, so keep that buffer alive while you use the results. No buffer, no treasure map.
## Core Types
### `fio_der_element_s`
```c
typedef struct {
const uint8_t *data;
size_t len;
uint8_t tag;
uint8_t is_constructed;
uint8_t tag_class;
uint8_t tag_number;
} fio_der_element_s;
```
A parsed DER element. `data` points to the content bytes, after the tag and length fields. `len` is the content length. The remaining fields describe the tag byte.
### `fio_der_iterator_s`
```c
typedef struct {
const uint8_t *pos;
const uint8_t *end;
} fio_der_iterator_s;
```
Iterator state for walking a `SEQUENCE` or `SET` element.
### Tags and Classes
`fio_der_tag_e` defines the common universal tags, including `FIO_DER_INTEGER`, `FIO_DER_BIT_STRING`, `FIO_DER_OID`, `FIO_DER_SEQUENCE`, `FIO_DER_SET`, string types, `FIO_DER_UTC_TIME`, `FIO_DER_GENERALIZED_TIME`, and context wrappers `FIO_DER_CONTEXT_0` through `FIO_DER_CONTEXT_3`.
`fio_der_class_e` names the tag classes: universal, application, context-specific, and private.
## OID Values
OID constants live in the X.509 module (`FIO_X509_OID_*`, see the X.509
documentation). An OID value is a plain `fio_u128` where bytes 0-14 hold the
DER content bytes (zero-padded) and byte 15 holds the content length. The
length is folded in because `0x00` is a legal OID content byte (arc 0), so
padding alone could not distinguish `{..0B}` from `{..0B 00}` - different
OIDs. Oversized OIDs (content > 15 bytes) simply never match, which is safe:
AlgorithmIdentifier OIDs are attacker-controlled and RFC 5280 requires exact
match.
Build a value once per parsed element with `fio___der_oid_value` (internal),
then compare it against any number of constants with `fio___der_oid_eq`
(internal) - two u64-lane `==` comparisons, no memcmp, no call:
```c
fio_u128 oid = fio___der_oid_value(&elem); /* build ONCE per element */
if (fio___der_oid_eq(oid, FIO_X509_OID_COMMON_NAME)) { /* ... */ }
```
## Parsing
### `fio_der_parse`
```c
SFUNC const uint8_t *fio_der_parse(fio_der_element_s *elem,
const uint8_t *data,
size_t data_len);
```
Parses one DER element. On success, fills `elem` and returns a pointer to the next element. On error, returns `NULL`.
It rejects invalid DER lengths, truncated buffers, unsupported high-tag-number encodings, and indefinite lengths.
### `fio_der_element_total_len`
```c
FIO_IFUNC size_t fio_der_element_total_len(const fio_der_element_s *elem,
const uint8_t *data);
```
Returns the full encoded length of an element: tag + length + content. `data` must be the original pointer used to parse the element.
## Type Parsers
### `fio_der_parse_integer`
```c
SFUNC int fio_der_parse_integer(const fio_der_element_s *elem,
uint64_t *value);
```
Parses an ASN.1 `INTEGER`. For small integers, pass `value` and receive the 64-bit value. For large integers such as RSA moduli, pass `NULL` and use `elem->data` / `elem->len` directly. Leading zero bytes for positive integers are handled.
Returns `0` on success, `-1` on error.
### `fio_der_parse_bit_string`
```c
SFUNC int fio_der_parse_bit_string(const fio_der_element_s *elem,
const uint8_t **bits,
size_t *bit_len,
uint8_t *unused_bits);
```
Parses an ASN.1 `BIT STRING`. `bits` points into the element data after the unused-bit count byte. `bit_len` is the byte length of the bit payload. `unused_bits` is the number of unused bits in the last byte.
### `fio_der_parse_oid`
```c
SFUNC int fio_der_parse_oid(const fio_der_element_s *elem,
char *buf,
size_t buf_len);
```
Converts an ASN.1 `OBJECT IDENTIFIER` into dotted text such as `1.2.840.113549.1.1.11`.
Returns the number of characters written, excluding the NUL byte, or `-1` on error.
### `fio_der_parse_time`
```c
SFUNC int fio_der_parse_time(const fio_der_element_s *elem,
int64_t *unix_time);
```
Parses `UTCTime` or `GeneralizedTime` into a Unix timestamp. Times must be UTC (`Z`). Fractional seconds are skipped when present.
### `fio_der_parse_string`
```c
FIO_IFUNC const char *fio_der_parse_string(const fio_der_element_s *elem,
size_t *len);
```
Returns a pointer to the string bytes for supported ASN.1 string tags, with the byte length in `len`. This does not validate or transcode the text; it simply gives you the payload.
### `fio_der_parse_boolean`
```c
FIO_IFUNC int fio_der_parse_boolean(const fio_der_element_s *elem,
int *value);
```
Parses a DER boolean and writes `0` or non-zero to `value`.
## Walking Sequences and Sets
### `fio_der_iterator_init`
```c
FIO_IFUNC void fio_der_iterator_init(fio_der_iterator_s *it,
const fio_der_element_s *sequence);
```
Initializes an iterator over the content bytes of a parsed `SEQUENCE` or `SET`.
### `fio_der_iterator_next`
```c
SFUNC int fio_der_iterator_next(fio_der_iterator_s *it,
fio_der_element_s *elem);
```
Parses the next child element and advances the iterator. Returns `0` when an element was read, `-1` at end or on parse error.
### `fio_der_iterator_has_next`
```c
FIO_IFUNC int fio_der_iterator_has_next(const fio_der_iterator_s *it);
```
Returns `1` when the iterator still has bytes to parse, otherwise `0`.
## Tag Helpers
```c
FIO_IFUNC int fio_der_is_tag(const fio_der_element_s *elem, uint8_t tag);
FIO_IFUNC int fio_der_is_context_tag(const fio_der_element_s *elem,
uint8_t tag_num);
FIO_IFUNC uint8_t fio_der_tag_number(const fio_der_element_s *elem);
```
Use these to keep parser code readable:
- `fio_der_is_tag` checks a universal tag such as `FIO_DER_INTEGER`.
- `fio_der_is_context_tag` checks `[0]`, `[1]`, and friends.
- `fio_der_tag_number` returns the decoded tag number.
## Encoding
All encoder functions return the number of bytes written or needed. Pass `NULL` as `buf` to calculate the encoded length before writing.
### Basic Encoders
```c
SFUNC size_t fio_der_encode_length(uint8_t *buf, size_t len);
SFUNC size_t fio_der_encode_integer(uint8_t *buf,
const uint8_t *data,
size_t data_len);
SFUNC size_t fio_der_encode_integer_small(uint8_t *buf, uint64_t value);
SFUNC size_t fio_der_encode_null(uint8_t *buf);
SFUNC size_t fio_der_encode_boolean(uint8_t *buf, int value);
```
These write DER length fields, positive integers, `NULL`, and booleans. `fio_der_encode_integer` expects big-endian integer bytes and adds the leading zero byte when DER needs one to keep the integer positive.
OIDs are encoded with the internal `fio___der_encode_oid(buf, fio_u128 oid)`,
which TLV-wraps an OID value (see OID Values above) without any dot-string
parsing.
### String and Byte Encoders
```c
SFUNC size_t fio_der_encode_utf8_string(uint8_t *buf,
const char *str,
size_t str_len);
SFUNC size_t fio_der_encode_printable_string(uint8_t *buf,
const char *str,
size_t str_len);
SFUNC size_t fio_der_encode_bit_string(uint8_t *buf,
const uint8_t *bits,
size_t bit_len,
uint8_t unused_bits);
SFUNC size_t fio_der_encode_octet_string(uint8_t *buf,
const uint8_t *data,
size_t data_len);
```
These write the tag, DER length, and payload for common primitive values.
### Header Encoders
```c
SFUNC size_t fio_der_encode_sequence_header(uint8_t *buf, size_t content_len);
SFUNC size_t fio_der_encode_set_header(uint8_t *buf, size_t content_len);
SFUNC size_t fio_der_encode_context_header(uint8_t *buf,
uint8_t tag_num,
size_t content_len,
int constructed);
```
These write only the wrapper tag and length. Write the content bytes immediately after the returned header length.
### Time Encoders
```c
SFUNC size_t fio_der_encode_utc_time(uint8_t *buf, int64_t unix_time);
SFUNC size_t fio_der_encode_generalized_time(uint8_t *buf, int64_t unix_time);
```
These encode Unix timestamps as DER `UTCTime` or `GeneralizedTime`.
## Example
```c
#define FIO_DER
#include "fio-stl.h"
void scan_sequence(const uint8_t *der, size_t der_len) {
fio_der_element_s seq;
if (!fio_der_parse(&seq, der, der_len))
return;
if (!fio_der_is_tag(&seq, FIO_DER_SEQUENCE))
return;
fio_der_iterator_s it;
fio_der_iterator_init(&it, &seq);
fio_der_element_s elem;
while (fio_der_iterator_next(&it, &elem) == 0) {
if (fio_der_is_tag(&elem, FIO_DER_OID)) {
fio_u128 oid = fio___der_oid_value(&elem);
/* compare with FIO_X509_OID_* constants via fio___der_oid_eq, or */
char dot[128];
if (fio_der_parse_oid(&elem, dot, sizeof(dot)) > 0) {
/* dotted string available for diagnostics */
}
}
}
}
```
------------------------------------------------------------
# RSA Signatures
```c
#define FIO_RSA
#include "fio-stl.h"
```
RSA here means signature work for TLS and X.509: verify PKCS#1 v1.5 signatures, verify RSA-PSS signatures, and create RSA-PSS signatures for TLS 1.3 `CertificateVerify`.
Supported modulus sizes are 2048, 3072, and 4096 bits. Smaller keys do not get a party hat.
**Security note:** this implementation has not been independently audited. Prefer a tested crypto library for high-value private keys, compliance, HSM integration, or anything that keeps auditors awake.
## Constants and Hash IDs
```c
#define FIO_RSA_MAX_BITS 4096
#define FIO_RSA_MAX_BYTES (FIO_RSA_MAX_BITS / 8)
#define FIO_RSA_MAX_WORDS (FIO_RSA_MAX_BYTES / 8)
```
```c
typedef enum {
FIO_RSA_HASH_SHA256 = 0,
FIO_RSA_HASH_SHA384 = 1,
FIO_RSA_HASH_SHA512 = 2,
} fio_rsa_hash_e;
```
`hash_len` must match the selected hash: 32 bytes for SHA-256, 48 for SHA-384, and 64 for SHA-512.
## Key Types
### `fio_rsa_pubkey_s`
```c
typedef struct {
const uint8_t *n;
size_t n_len;
const uint8_t *e;
size_t e_len;
} fio_rsa_pubkey_s;
```
Public key for verification. `n` is the modulus and `e` is the public exponent, both as big-endian byte arrays. This matches the DER layout used in X.509 certificates.
### `fio_rsa_privkey_s`
```c
typedef struct {
const uint8_t *n; size_t n_len;
const uint8_t *d; size_t d_len;
const uint8_t *e; size_t e_len;
const uint8_t *p; size_t p_len;
const uint8_t *q; size_t q_len;
const uint8_t *dP; size_t dP_len;
const uint8_t *dQ; size_t dQ_len;
const uint8_t *qInv; size_t qInv_len;
} fio_rsa_privkey_s;
```
Private key for RSA-PSS signing. `n` and `d` are required. The public exponent `e` and CRT fields (`p`, `q`, `dP`, `dQ`, `qInv`) are optional.
When CRT parameters are available, signing uses CRT with message blinding. If only `n` and `d` are available, signing falls back to the non-CRT path.
## Verification
### `fio_rsa_verify_pkcs1`
```c
SFUNC int fio_rsa_verify_pkcs1(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_pubkey_s *key);
```
Verifies an RSA PKCS#1 v1.5 signature with `DigestInfo` encoding.
This covers the X.509 signature algorithms commonly named:
- `sha256WithRSAEncryption`
- `sha384WithRSAEncryption`
- `sha512WithRSAEncryption`
Parameters:
- `sig`: signature bytes; length must match the modulus length.
- `sig_len`: signature length in bytes.
- `msg_hash`: pre-computed message hash.
- `hash_len`: 32, 48, or 64 bytes.
- `hash_alg`: matching `FIO_RSA_HASH_*` value.
- `key`: RSA public key.
Returns `0` for a valid signature, `-1` on invalid input or a bad signature.
### `fio_rsa_verify_pss`
```c
SFUNC int fio_rsa_verify_pss(const uint8_t *sig,
size_t sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_pubkey_s *key);
```
Verifies an RSA-PSS signature. This is the RSA signature style TLS 1.3 uses for `CertificateVerify`.
The implementation uses:
- MGF1 with the same hash function;
- salt length equal to the hash length;
- trailer byte `0xBC`.
Returns `0` for a valid signature, `-1` on invalid input or a bad signature.
## Signing
### `fio_rsa_sign_pss`
```c
SFUNC int fio_rsa_sign_pss(uint8_t *signature,
size_t *sig_len,
const uint8_t *msg_hash,
size_t hash_len,
fio_rsa_hash_e hash_alg,
const fio_rsa_privkey_s *key);
```
Creates an RSA-PSS signature using `RSASSA-PSS-SIGN` from RFC 8017, with the TLS 1.3 settings listed above.
- `signature` must have room for `key->n_len` bytes.
- `sig_len` receives the signature length, which equals `key->n_len` on success.
- `msg_hash` is already hashed; this function does not hash the message.
- `key` must include `n` and `d`; CRT fields are optional.
Returns `0` on success, `-1` on error.
PKCS#1 v1.5 signing is not exposed. TLS 1.3 does not want it for `CertificateVerify`, and this header agrees.
## Example: Verify an RSA-PSS Signature
```c
#define FIO_RSA
#define FIO_SHA2
#include "fio-stl.h"
int verify_pss(const uint8_t *sig,
size_t sig_len,
const uint8_t *modulus,
size_t modulus_len,
const uint8_t *exponent,
size_t exponent_len,
const void *message,
size_t message_len) {
fio_rsa_pubkey_s key = {
.n = modulus,
.n_len = modulus_len,
.e = exponent,
.e_len = exponent_len,
};
fio_u256 hash = fio_sha256(message, message_len);
return fio_rsa_verify_pss(sig, sig_len, hash.u8, 32,
FIO_RSA_HASH_SHA256, &key);
}
```
## Practical Notes
- The signature length must match the modulus length.
- The public exponent is usually `65537` (`0x010001`), but the API accepts DER-style big-endian bytes.
- The module works on pre-computed hashes. Pick the hash to match the certificate or protocol signature algorithm.
- For X.509 parsing, this module is normally used through [X.509](https://facil.io/edge/http-1-x-parser/), which extracts the RSA key and dispatches verification.
------------------------------------------------------------
# ML-KEM-768
```c
#define FIO_MLKEM
#include "fio-stl.h"
```
ML-KEM-768 is the FIPS 203 module-lattice key encapsulation mechanism: a post-quantum way to agree on a shared secret. This header also exposes the TLS hybrid `X25519MLKEM768` shape, which combines ML-KEM-768 with X25519.
**Security note:** this implementation has not been independently audited. Use it carefully, and prefer audited crypto stacks for high-value deployments.
## ML-KEM-768 Sizes
```c
#define FIO_MLKEM768_PUBLICKEYBYTES 1184
#define FIO_MLKEM768_SECRETKEYBYTES 2400
#define FIO_MLKEM768_CIPHERTEXTBYTES 1088
#define FIO_MLKEM768_SSBYTES 32
#define FIO_MLKEM768_SYMBYTES 32
```
Parameters: `n = 256`, `k = 3`, `q = 3329`, `eta1 = 2`, `eta2 = 2`, `d_u = 10`, `d_v = 4`.
## ML-KEM-768 API
### `fio_mlkem768_keypair`
```c
SFUNC int fio_mlkem768_keypair(uint8_t pk[1184], uint8_t sk[2400]);
```
Generates a random ML-KEM-768 key pair using the system CSPRNG. Returns `0` on success, `-1` on failure.
### `fio_mlkem768_keypair_derand`
```c
SFUNC int fio_mlkem768_keypair_derand(uint8_t pk[1184],
uint8_t sk[2400],
const uint8_t coins[64]);
```
Generates a deterministic key pair. `coins` is exactly 64 bytes: `d || z`. This is mostly for test vectors and reproducible checks; production code should normally use `fio_mlkem768_keypair`.
### `fio_mlkem768_encaps`
```c
SFUNC int fio_mlkem768_encaps(uint8_t ct[1088],
uint8_t ss[32],
const uint8_t pk[1184]);
```
Creates a ciphertext and shared secret for the holder of `pk`. Randomness comes from the system CSPRNG. Send `ct`; keep `ss` as the shared secret input to your KDF or protocol.
### `fio_mlkem768_encaps_derand`
```c
SFUNC int fio_mlkem768_encaps_derand(uint8_t ct[1088],
uint8_t ss[32],
const uint8_t pk[1184],
const uint8_t coins[32]);
```
Deterministic encapsulation using 32 bytes of caller-provided randomness. Useful for known-answer tests. Boring in production, which is exactly the point.
### `fio_mlkem768_decaps`
```c
SFUNC int fio_mlkem768_decaps(uint8_t ss[32],
const uint8_t ct[1088],
const uint8_t sk[2400]);
```
Recovers the shared secret from a ciphertext and secret key.
ML-KEM uses implicit rejection: invalid ciphertexts produce a pseudorandom shared secret derived from the secret key and ciphertext instead of a loud failure. That prevents chosen-ciphertext games from turning your error path into an oracle.
Returns `0` for well-formed inputs.
## X25519MLKEM768 Hybrid
`X25519MLKEM768` is the TLS 1.3 hybrid key exchange shape from the ECDHE/ML-KEM draft. It needs `FIO_ED25519` too, because this STL implements X25519 in the Ed25519/Curve25519 header.
The name starts with X25519, but the bytes start with ML-KEM. Yes, naming is hard.
```c
#define FIO_X25519MLKEM768_PUBLICKEYBYTES (32 + 1184) /* 1216 */
#define FIO_X25519MLKEM768_SECRETKEYBYTES (32 + 2400) /* 2432 */
#define FIO_X25519MLKEM768_CIPHERTEXTBYTES (32 + 1088) /* 1120 */
#define FIO_X25519MLKEM768_SSBYTES 64
```
| Value | Layout |
| --- | --- |
| Public key | `ML-KEM-768_ek` (1184) `||` `X25519_pk` (32) |
| Secret key | `ML-KEM-768_dk` (2400) `||` `X25519_sk` (32) |
| Ciphertext | `ML-KEM-768_ct` (1088) `||` `X25519_ephemeral_pk` (32) |
| Shared secret | `ML-KEM-768_ss` (32) `||` `X25519_ss` (32) |
### `fio_x25519mlkem768_keypair`
```c
SFUNC int fio_x25519mlkem768_keypair(uint8_t pk[1216], uint8_t sk[2432]);
```
Generates both the ML-KEM-768 and X25519 key pairs with system randomness. Returns `0` on success.
### `fio_x25519mlkem768_encaps`
```c
SFUNC int fio_x25519mlkem768_encaps(uint8_t ct[1120],
uint8_t ss[64],
const uint8_t pk[1216]);
```
Performs ML-KEM encapsulation and X25519 ephemeral key exchange. The returned shared secret is the concatenation `ML-KEM-768_ss || X25519_ss`.
### `fio_x25519mlkem768_decaps`
```c
SFUNC int fio_x25519mlkem768_decaps(uint8_t ss[64],
const uint8_t ct[1120],
const uint8_t sk[2432]);
```
Decapsulates the ML-KEM part and computes the X25519 shared secret. Returns `0` on success, or `-1` if X25519 rejects the peer point. ML-KEM invalid ciphertexts still use implicit rejection.
## Example
```c
#define FIO_MLKEM
#include "fio-stl.h"
int roundtrip(void) {
uint8_t pk[FIO_MLKEM768_PUBLICKEYBYTES];
uint8_t sk[FIO_MLKEM768_SECRETKEYBYTES];
uint8_t ct[FIO_MLKEM768_CIPHERTEXTBYTES];
uint8_t sender_ss[FIO_MLKEM768_SSBYTES];
uint8_t receiver_ss[FIO_MLKEM768_SSBYTES];
if (fio_mlkem768_keypair(pk, sk))
return -1;
if (fio_mlkem768_encaps(ct, sender_ss, pk))
return -1;
if (fio_mlkem768_decaps(receiver_ss, ct, sk))
return -1;
return fio_memcmp(sender_ss, receiver_ss, sizeof(sender_ss));
}
```
## Implementation Notes
The implementation uses ML-KEM-768 parameters, NTT arithmetic, Montgomery and Barrett reduction, and optional NEON / AVX2 paths for vectorized polynomial work. Those SIMD paths are speed knobs, not different APIs.
------------------------------------------------------------
# X.509 Certificates
```c
#define FIO_X509
#include "fio-stl.h"
```
This module parses DER-encoded X.509v3 certificates for TLS 1.3 style certificate checks. It is a compact parser, verifier, trust-store checker, TLS Certificate-message splitter, and small self-signed certificate generator.
It is non-allocating while parsing: `fio_x509_cert_s` stores pointers into the original DER bytes. Keep those bytes alive. Certificates dislike disappearing floors.
**Scope note:** this is a minimal TLS-oriented X.509 implementation, not a full PKI policy engine. Not all X.509 features are supported.
## Supported Pieces
- RSA, ECDSA P-256, ECDSA P-384, and Ed25519 public keys.
- RSA PKCS#1 v1.5, RSA-PSS, ECDSA, and Ed25519 signature verification.
- Validity period checks.
- Hostname matching using SAN DNS names and CN fallback, with one-label wildcards.
- Basic Constraints and Key Usage.
- Certificate chain validation against an optional trust store.
- TLS 1.3 Certificate message parsing.
- Self-signed certificate generation for Ed25519 and P-256 keys.
## Main Types
### Key and Signature Enums
```c
typedef enum {
FIO_X509_KEY_UNKNOWN = 0,
FIO_X509_KEY_RSA = 1,
FIO_X509_KEY_ECDSA_P256 = 2,
FIO_X509_KEY_ECDSA_P384 = 3,
FIO_X509_KEY_ED25519 = 4,
} fio_x509_key_algo_e;
```
```c
typedef enum {
FIO_X509_SIGNATURE_UNKNOWN = 0,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA256 = 1,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA384 = 2,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA512 = 3,
FIO_X509_SIGNATURE_RSA_PSS_SHA256 = 4,
FIO_X509_SIGNATURE_RSA_PSS_SHA384 = 5,
FIO_X509_SIGNATURE_RSA_PSS_SHA512 = 6,
FIO_X509_SIGNATURE_ECDSA_SHA256 = 7,
FIO_X509_SIGNATURE_ECDSA_SHA384 = 8,
FIO_X509_SIGNATURE_ED25519 = 9,
} fio_x509_signature_algo_e;
```
### Key Usage and Errors
`fio_x509_key_usage_e` defines RFC 5280 key-usage bits such as `FIO_X509_KU_DIGITAL_SIGNATURE`, `FIO_X509_KU_KEY_ENCIPHERMENT`, and `FIO_X509_KU_KEY_CERT_SIGN`. ASN.1 bit strings use MSB-first ordering, so the constants look a little backwards until they save you from off-by-one sadness.
`fio_x509_error_e` gives chain validation results: `FIO_X509_OK`, parse failure, expired/not-yet-valid, signature failure, issuer mismatch, not-a-CA, no trust anchor, hostname mismatch, empty chain, and chain-too-long.
### `fio_x509_trust_store_s`
```c
typedef struct {
const uint8_t **roots;
const size_t *root_lens;
size_t root_count;
} fio_x509_trust_store_s;
```
A simple root store: arrays of DER certificate pointers and lengths.
### `fio_tls_cert_entry_s`
```c
typedef struct {
const uint8_t *cert;
size_t cert_len;
} fio_tls_cert_entry_s;
```
One certificate entry extracted from a TLS 1.3 `Certificate` handshake message.
### `fio_x509_cert_s`
```c
typedef struct fio_x509_cert_s fio_x509_cert_s;
```
The parsed certificate structure uses non-owning buffer views
(`fio_buf_info_s` / `fio_ubuf_info_s`) and packs to 256 bytes with no
interior padding:
- `verified`: non-zero when a TLS backend verified this certificate's chain
(peer inspection only; always zero after `fio_x509_parse`);
- `der`: a reference to the original DER bytes (not a copy);
- `fingerprint`: 32-byte SHA-256 of the DER bytes (see `fio_x509_fingerprint`);
- `version`, `serial`, and validity timestamps;
- raw subject and issuer DNs;
- subject CN;
- public key type and key data;
- signature algorithm and signature bytes;
- TBS certificate bytes used for verification;
- Basic Constraints (`is_ca`), Key Usage (`has_key_usage`, `key_usage`), and
SAN data (first DNS name, first IP address, and the raw SAN extension for
iterating the rest).
All view pointers reference the original DER buffer.
## Parsing and Single-Certificate Checks
### `fio_x509_parse`
```c
SFUNC int fio_x509_parse(fio_x509_cert_s *cert,
const uint8_t *der_data,
size_t der_len);
```
Parses one DER certificate. `cert` is zeroed first. Returns `0` on success, `-1` on parse error.
### `fio_x509_fingerprint`
```c
SFUNC void fio_x509_fingerprint(fio_x509_cert_s *cert);
```
Computes the SHA-256 of the certificate's DER bytes into
`cert->fingerprint` (32 raw bytes). Call after `fio_x509_parse`; hashing is
lazy so parsing never pays for it. Requires the SHA2 module.
### `fio_x509_verify_signature`
```c
SFUNC int fio_x509_verify_signature(const fio_x509_cert_s *cert,
const fio_x509_cert_s *issuer);
```
Verifies that `issuer` signed `cert`, using the issuer public key and the certificate signature algorithm. Returns `0` when the signature is valid, `-1` otherwise.
### `fio_x509_check_validity`
```c
FIO_IFUNC int fio_x509_check_validity(const fio_x509_cert_s *cert,
int64_t current_time);
```
Checks `not_before <= current_time <= not_after`. Returns `0` when the certificate is in its validity window.
### `fio_x509_match_hostname`
```c
SFUNC int fio_x509_match_hostname(const fio_x509_cert_s *cert,
const char *hostname,
size_t hostname_len);
```
Checks whether a certificate matches a hostname. SAN DNS entries are supported, with CN fallback. Wildcards are one-label wildcards such as `*.example.com`; they do not swallow `a.b.example.com`.
Returns `0` for a match, `-1` for no match.
Distinguished Names are compared as raw DER with the core
`FIO_BUF_INFO_IS_EQ` macro (e.g.,
`FIO_BUF_INFO_IS_EQ(cert.issuer, root.subject)`).
## Chain Validation
### `fio_x509_verify_chain`
```c
SFUNC int fio_x509_verify_chain(const uint8_t **certs,
const size_t *cert_lens,
size_t cert_count,
const char *hostname,
int64_t current_time,
fio_x509_trust_store_s *trust_store);
```
Validates a certificate chain for TLS-style use.
Expected order:
1. `certs[0]`: end-entity / server certificate.
2. `certs[1]`: intermediate that signed `certs[0]`.
3. `certs[n-1]`: closest-to-root certificate.
Validation parses all certificates, checks validity periods, optionally matches the hostname, verifies each signature with the next certificate, checks issuer/subject DN links, requires CA certificates where needed, and optionally checks the final certificate against `trust_store`.
Trust anchors are matched by (subject DN, public key): the anchor's key must
verify the final certificate's signature. This also covers self-signed
certificates — a same-named impostor with a different key is rejected.
Returns `FIO_X509_OK` (`0`) on success, or a `FIO_X509_ERR_*` code.
### `fio_x509_is_trusted`
```c
SFUNC int fio_x509_is_trusted(const fio_x509_cert_s *cert,
fio_x509_trust_store_s *trust_store);
```
Checks whether `cert` appears in the trust store by subject DN match. Returns `0` if trusted, `-1` if not found.
### `fio_x509_error_str`
```c
FIO_IFUNC const char *fio_x509_error_str(int error);
```
Returns a static string for a chain validation error code.
## TLS Certificate Messages
```c
#define FIO_TLS_CERT_PARSE_ERROR ((size_t)-1)
```
### `fio_tls_parse_certificate_message`
```c
SFUNC size_t fio_tls_parse_certificate_message(fio_tls_cert_entry_s *entries,
size_t max_entries,
const uint8_t *data,
size_t data_len);
```
Parses a TLS 1.3 `Certificate` message body, after the handshake header. It extracts certificate entries and skips per-certificate extensions.
Returns the number of certificates parsed, or `FIO_TLS_CERT_PARSE_ERROR` on malformed input.
## Self-Signed Certificate Generation
Generation supports Ed25519 and P-256 key pairs.
```c
typedef enum {
FIO_X509_KEYPAIR_ED25519 = 1,
FIO_X509_KEYPAIR_P256 = 2,
} fio_x509_keypair_type_e;
```
`fio_x509_keypair_s` stores the selected key type, secret key bytes, public key bytes, and their lengths. Use `fio_x509_keypair_clear` when done.
`fio_x509_cert_options_s` controls the subject CN, organization, organizational unit, country, validity window, SAN DNS names, CA flag, and key-usage bits.
### `fio_x509_keypair_ed25519`
```c
SFUNC int fio_x509_keypair_ed25519(fio_x509_keypair_s *keypair);
```
Generates an Ed25519 key pair for certificate signing. Requires the Ed25519 module to be included. Returns `0` on success.
### `fio_x509_keypair_p256`
```c
SFUNC int fio_x509_keypair_p256(fio_x509_keypair_s *keypair);
```
Generates a P-256 key pair for certificate signing. Requires the P-256 module to be included. Returns `0` on success.
### `fio_x509_self_signed_cert`
```c
SFUNC size_t fio_x509_self_signed_cert(uint8_t *buf,
size_t buf_len,
const fio_x509_keypair_s *keypair,
const fio_x509_cert_options_s *options);
```
Writes a DER-encoded self-signed X.509v3 certificate. Call with `buf == NULL` to get a worst-case buffer size, then call again with a real buffer.
Returns bytes written, or `0` on error.
### `fio_x509_keypair_clear`
```c
FIO_IFUNC void fio_x509_keypair_clear(fio_x509_keypair_s *keypair);
```
Securely clears key material and zeros the structure.
## Example: Parse and Check Hostname
```c
#define FIO_X509
#include "fio-stl.h"
int check_cert(const uint8_t *der, size_t der_len, int64_t now) {
fio_x509_cert_s cert;
if (fio_x509_parse(&cert, der, der_len))
return -1;
if (fio_x509_check_validity(&cert, now))
return -1;
if (fio_x509_match_hostname(&cert, "example.com", 11))
return -1;
return 0;
}
```
## Practical Notes
- Define the crypto modules you need before including the STL: RSA, P-256, P-384, Ed25519, SHA-2, and ASN.1 may all matter depending on certificate algorithms.
- `trust_store == NULL` skips root trust checking in `fio_x509_verify_chain`; useful for structural tests, not for real trust decisions.
- Hostname checks are DNS-name focused. IP address SAN handling is not a full replacement for platform certificate validation.
- Parsed strings and DNs are raw certificate data. Normalize policy elsewhere if your application needs browser-grade PKI behavior.
------------------------------------------------------------
# PEM Parser
```c
#define FIO_PEM
#include "fio-stl.h"
```
PEM wraps DER bytes in Base64 armor:
```text
-----BEGIN -----
-----END -----
```
This module extracts PEM blocks, decodes certificates, and parses private keys used by the TLS/X.509 helpers.
**Limit:** encrypted private keys are not supported. If the key asks for a password, this parser politely leaves the room.
## Supported Labels
- `CERTIFICATE`
- `PRIVATE KEY` (PKCS#8)
- `RSA PRIVATE KEY` (legacy PKCS#1)
- `EC PRIVATE KEY` (legacy SEC1)
Private-key parsing supports RSA, P-256, and Ed25519 where the required crypto modules are available.
## Types
### `fio_pem_key_type_e`
```c
typedef enum {
FIO_PEM_KEY_UNKNOWN = 0,
FIO_PEM_KEY_RSA = 1,
FIO_PEM_KEY_ECDSA_P256 = 2,
FIO_PEM_KEY_ED25519 = 3,
} fio_pem_key_type_e;
```
### `fio_pem_s`
```c
typedef struct {
const uint8_t *der;
size_t der_len;
const char *label;
size_t label_len;
} fio_pem_s;
```
A decoded PEM block. `der` points into the caller-provided DER output buffer. `label` points into the original PEM input.
### `fio_pem_private_key_s`
```c
typedef struct fio_pem_private_key_s fio_pem_private_key_s;
```
Parsed private-key material. The active union member depends on `type`:
- RSA: fixed-size byte arrays for `n`, `e`, `d`, optional `p`, `q`, `dP`, `dQ`, and `qInv`, each with a length.
- P-256: 32-byte private scalar, optional 65-byte uncompressed public key.
- Ed25519: 32-byte seed, optional 32-byte public key.
Call `fio_pem_private_key_clear` when done.
## API
### `fio_pem_parse`
```c
SFUNC size_t fio_pem_parse(fio_pem_s *out,
uint8_t *der_buf,
size_t der_buf_len,
const char *pem_data,
size_t pem_len);
```
Finds the next PEM block, decodes its Base64 body into `der_buf`, and fills `out`.
Returns the number of bytes consumed from `pem_data`, or `0` on error. To parse a file with multiple PEM blocks, advance by the returned count and call again.
### `fio_pem_parse_certificate`
```c
SFUNC int fio_pem_parse_certificate(fio_x509_cert_s *cert,
const char *pem_data,
size_t pem_len);
```
Finds a `CERTIFICATE` block, decodes it, and parses the DER certificate with the X.509 parser. Returns `0` on success, `-1` on error.
Requires the X.509 module for the certificate parse.
### `fio_pem_parse_private_key`
```c
SFUNC int fio_pem_parse_private_key(fio_pem_private_key_s *key,
const char *pem_data,
size_t pem_len);
```
Parses a PEM private key into `key`.
Supported forms:
| PEM label | Format | Supported keys |
| --- | --- | --- |
| `PRIVATE KEY` | PKCS#8 | RSA, P-256, Ed25519 |
| `RSA PRIVATE KEY` | PKCS#1 | RSA |
| `EC PRIVATE KEY` | SEC1 | P-256 |
Returns `0` on success, `-1` on unsupported format, missing dependency, encrypted key, or malformed input.
### `fio_pem_get_certificate_der`
```c
SFUNC size_t fio_pem_get_certificate_der(uint8_t *der_out,
size_t der_out_len,
const char *pem_data,
size_t pem_len);
```
Extracts a `CERTIFICATE` block as raw DER without parsing the X.509 structure. Returns the DER length written, or `0` on error.
### `fio_pem_private_key_clear`
```c
FIO_IFUNC void fio_pem_private_key_clear(fio_pem_private_key_s *key);
```
Securely zeros a parsed private-key structure.
## Example: Extract Certificate DER
```c
#define FIO_PEM
#include "fio-stl.h"
size_t load_der(uint8_t *der, size_t der_cap, const char *pem, size_t pem_len) {
return fio_pem_get_certificate_der(der, der_cap, pem, pem_len);
}
```
## Example: Parse Multiple Blocks
```c
#define FIO_PEM
#include "fio-stl.h"
void scan_pem(const char *pem, size_t len) {
uint8_t der[8192];
while (len) {
fio_pem_s block;
size_t used = fio_pem_parse(&block, der, sizeof(der), pem, len);
if (!used)
break;
/* block.label / block.label_len name the PEM type. */
/* block.der / block.der_len hold the decoded DER bytes. */
pem += used;
len -= used;
}
}
```
## Dependency Notes
- Generic `fio_pem_parse` needs only the PEM/base64 helpers.
- Certificate parsing needs X.509 support.
- Private key parsing uses ASN.1, and RSA/P-256/Ed25519 support depends on the matching crypto headers.
------------------------------------------------------------
# Argon2
```c
#define FIO_ARGON2
#include "fio-stl.h"
```
Argon2 is a memory-hard password hashing function from RFC 9106. It uses BLAKE2b underneath and lets you tune time, memory, and lane count so attackers have to pay real rent.
This implementation supports Argon2d, Argon2i, and Argon2id. It is single-threaded: `parallelism` affects the Argon2 lane layout, but lanes are processed sequentially.
When comparing Argon2 outputs, use `fio_ct_is_eq` or another constant-time comparison.
## Types
### `fio_argon2_type_e`
```c
typedef enum {
FIO_ARGON2D = 0,
FIO_ARGON2I = 1,
FIO_ARGON2ID = 2,
} fio_argon2_type_e;
```
- `FIO_ARGON2D`: data-dependent access, faster, weaker against side-channel observers.
- `FIO_ARGON2I`: data-independent access.
- `FIO_ARGON2ID`: hybrid mode, commonly recommended for password hashing.
### `fio_argon2_args_s`
```c
typedef struct {
fio_buf_info_s password;
fio_buf_info_s salt;
fio_buf_info_s secret;
fio_buf_info_s ad;
uint32_t t_cost;
uint32_t m_cost;
uint32_t parallelism;
uint32_t outlen;
fio_argon2_type_e type;
} fio_argon2_args_s;
```
Arguments:
| Field | Meaning |
| --- | --- |
| `password` | Password / message `P`. |
| `salt` | Salt / nonce `S`; use a unique random salt. |
| `secret` | Optional secret key `K`. |
| `ad` | Optional associated data `X`. |
| `t_cost` | Pass count, minimum `1`. |
| `m_cost` | Memory cost in KiB, minimum `8 * parallelism`. |
| `parallelism` | Lane count, minimum `1`; processed sequentially here. |
| `outlen` | Output length in bytes, minimum `4`, default `32`. |
| `type` | `FIO_ARGON2D`, `FIO_ARGON2I`, or `FIO_ARGON2ID`. |
## API
### `fio_argon2`
```c
SFUNC fio_u512 fio_argon2(fio_argon2_args_s args);
#define fio_argon2(...) fio_argon2((fio_argon2_args_s){__VA_ARGS__})
```
Computes an Argon2 hash and returns up to 64 bytes in `fio_u512`. Use the first `outlen` bytes; if `outlen` is `0`, the default is 32 bytes.
For outputs longer than 64 bytes, use `fio_argon2_hash`.
### `fio_argon2_hash`
```c
SFUNC int fio_argon2_hash(void *out, fio_argon2_args_s args);
#define fio_argon2_hash(out, ...) \
fio_argon2_hash(out, (fio_argon2_args_s){__VA_ARGS__})
```
Writes the Argon2 output into a caller-provided buffer. Supports arbitrary output lengths of at least 4 bytes.
Returns `0` on success, `-1` on error.
## Example
```c
#define FIO_ARGON2
#include "fio-stl.h"
int check_password(const char *password,
size_t password_len,
const void *salt,
size_t salt_len,
const uint8_t expected[32]) {
fio_u512 hash = fio_argon2(
.password = FIO_BUF_INFO2((void *)password, password_len),
.salt = FIO_BUF_INFO2((void *)salt, salt_len),
.t_cost = 3,
.m_cost = 65536, /* 64 MiB */
.parallelism = 4,
.outlen = 32,
.type = FIO_ARGON2ID);
return fio_ct_is_eq(hash.u8, expected, 32) ? 0 : -1;
}
```
## Practical Notes
- Store the salt and cost settings with the password hash. Future you will not remember them. Future you never does.
- Pick `FIO_ARGON2ID` for password hashing unless you have a specific reason not to.
- Raise `m_cost` and `t_cost` until the slow path is acceptable for your login flow.
- Do not compare hashes with `memcmp`; use constant-time comparison.
------------------------------------------------------------
# Lyra2
```c
#define FIO_LYRA2
#include "fio-stl.h"
```
Lyra2 is a memory-hard password hashing scheme built around a BLAKE2b sponge. It lets you tune time cost and memory matrix size so password checks are cheap for you and annoying for attackers. A fair trade.
This implementation matches the reference C setup with `nPARALLEL == 1`, `SPONGE == 0`, and `RHO == 1`. It is single-threaded.
When comparing Lyra2 outputs, use `fio_ct_is_eq` or another constant-time comparison.
## Arguments
```c
typedef struct {
fio_buf_info_s password;
fio_buf_info_s salt;
uint64_t t_cost;
uint64_t m_cost;
size_t outlen;
size_t n_cols;
} fio_lyra2_args_s;
```
| Field | Meaning |
| --- | --- |
| `password` | Password to hash. |
| `salt` | Salt for the hash; use a unique random salt. |
| `t_cost` | Time cost / number of rounds, minimum `1`. |
| `m_cost` | Memory cost / number of matrix rows, minimum `3`. |
| `outlen` | Output length in bytes; default `32` when `0`. |
| `n_cols` | Matrix column count; default `256` when `0`. |
## API
### `fio_lyra2`
```c
SFUNC fio_u512 fio_lyra2(fio_lyra2_args_s args);
#define fio_lyra2(...) fio_lyra2((fio_lyra2_args_s){__VA_ARGS__})
```
Computes a Lyra2 hash and returns up to 64 bytes in `fio_u512`. Use the first `outlen` bytes; when `outlen` is `0`, use the default 32 bytes.
For outputs longer than 64 bytes, use `fio_lyra2_hash`.
### `fio_lyra2_hash`
```c
SFUNC int fio_lyra2_hash(void *out, fio_lyra2_args_s args);
#define fio_lyra2_hash(out, ...) \
fio_lyra2_hash(out, (fio_lyra2_args_s){__VA_ARGS__})
```
Writes the Lyra2 output into a caller-provided buffer. Supports arbitrary output lengths.
Returns `0` on success, `-1` on error.
## Example
```c
#define FIO_LYRA2
#include "fio-stl.h"
int check_password(const char *password,
size_t password_len,
const void *salt,
size_t salt_len,
const uint8_t expected[32]) {
fio_u512 hash = fio_lyra2(
.password = FIO_BUF_INFO2((void *)password, password_len),
.salt = FIO_BUF_INFO2((void *)salt, salt_len),
.t_cost = 3,
.m_cost = 1024,
.outlen = 32);
return fio_ct_is_eq(hash.u8, expected, 32) ? 0 : -1;
}
```
## Practical Notes
- Store the salt, `t_cost`, `m_cost`, `n_cols`, and `outlen` next to the hash.
- Increase `m_cost` first when you want more memory pressure.
- Use constant-time comparison for verification.
- If you need the standardized RFC 9106 family, see [Argon2](https://facil.io/edge/argon2/). Lyra2 is a different memory-hard tool.
------------------------------------------------------------
# OTP (TOTP)
```c
#define FIO_OTP
#include "fio-stl.h"
```
Time-based One-Time Password helper. Implements the TOTP algorithm over HMAC-SHA-1, compatible with common authenticator apps. It can generate keys, encode them as Base32, and compute codes for the current time or a fixed timestamp.
### Types
#### `fio_otp_settings_s`
```c
typedef struct {
/** The time interval for TOTP rotation. */
size_t interval; /* 30 == Google OTP */
/** The number of digits in the OTP. */
size_t digits; /* 6 == Google OTP */
/** The time offset (in `interval` units) from the current time. */
int64_t offset; /* 0 == Google OTP */
/** Set to true if the secret / key is in Hex instead of Base32. */
uint8_t is_hex;
/** Set to true if the secret / key is raw bit data (no encoding). */
uint8_t is_raw;
} fio_otp_settings_s;
```
Configuration for OTP generation. Fields omitted in the named-argument macro call default to zero; the implementation then substitutes the Google-OTP defaults (30-second interval, 6 digits, zero offset).
### API Functions
#### `fio_otp_generate_key`
```c
FIO_IFUNC fio_u128 fio_otp_generate_key(void);
```
Generates a cryptographically secure random 128-bit key.
**Returns:** a 16-byte random key suitable for TOTP secrets.
#### `fio_otp_print_key`
```c
FIO_IFUNC size_t fio_otp_print_key(char *dest, uint8_t *key, size_t len);
```
Encodes a key as a Base32 string for sharing with authenticator apps.
**Parameters:**
- `dest` — output buffer. Must be at least `len * 2 + 1` bytes.
- `key` — key bytes; if `NULL`, a new random key is generated and encoded.
- `len` — key length in bytes.
**Returns:** length of the encoded string written to `dest`.
#### `fio_otp`
```c
SFUNC uint32_t fio_otp(fio_buf_info_s secret, fio_otp_settings_s settings);
/* Named arguments using macro. */
#define fio_otp(secret, ...) fio_otp(secret, (fio_otp_settings_s){__VA_ARGS__})
```
Computes a TOTP for the current wall-clock time.
**Parameters:**
- `secret` — shared secret as a `fio_buf_info_s`.
**Named Arguments:**
| Argument | Type | Description |
|---|---|---|
| `interval` | `size_t` | rotation interval in seconds; defaults to 30 |
| `digits` | `size_t` | number of output digits; defaults to 6 |
| `offset` | `int64_t` | interval offset from now; defaults to 0 |
| `is_hex` | `uint8_t` | set if `secret` is Hex encoded |
| `is_raw` | `uint8_t` | set if `secret` is raw bytes |
**Returns:** the OTP value.
#### `fio_otp_at`
```c
SFUNC uint32_t fio_otp_at(fio_buf_info_s secret,
uint64_t unix_time,
fio_otp_settings_s settings);
/* Named arguments using macro. */
#define fio_otp_at(secret, unix_time, ...) \
fio_otp_at(secret, unix_time, (fio_otp_settings_s){__VA_ARGS__})
```
Computes a TOTP for a specific Unix timestamp. Useful for tests and RFC vectors.
**Parameters:**
- `secret` — shared secret as a `fio_buf_info_s`.
- `unix_time` — timestamp to compute the OTP for.
**Named Arguments:** same as `fio_otp`.
**Returns:** the OTP value.
### Example
```c
#define FIO_OTP
#include "fio-stl.h"
int main(void) {
/* Generate and print a new Base32 secret. */
char key_str[64];
fio_otp_print_key(key_str, NULL, 0);
printf("secret: %s\n", key_str);
/* Compute the current 6-digit code. */
uint32_t code = fio_otp(FIO_BUF_INFO1(key_str));
printf("code: %06u\n", code);
return 0;
}
```
------------------------------------------------------------
# Secrets
```c
#define FIO_SECRET
#include "fio-stl.h"
```
A small program-wide secret helper. It reads an optional `SECRET` environment variable at startup, hashes it with SHA-512, and stores the result masked in memory so the raw secret does not show up in core dumps. If no secret is provided, a random one is generated.
This is **not** a key-management vault — it is a convenience for deriving a stable program-wide token from something outside the source tree.
### Environment Variables
At startup (via `FIO_CONSTRUCTOR`), the module checks:
- `SECRET` — the secret value, plain text or Hex encoded.
- `SECRET_LENGTH` — optional byte length; if omitted, `strlen` is used.
If `SECRET` is unset or empty, a random 512-bit secret is generated.
### API Functions
#### `fio_secret_is_random`
```c
SFUNC bool fio_secret_is_random(void);
```
Returns `true` if the global secret was randomly generated (rather than loaded from the environment).
#### `fio_secret`
```c
SFUNC fio_u512 fio_secret(void);
```
Returns the global secret as a masked SHA-512 hash. The value is unmasked on return; keep the result on the stack and erase it promptly.
**Returns:** the SHA-512 hash of the program secret, unmasked.
#### `fio_secret_set`
```c
SFUNC void fio_secret_set(char *str, size_t len, bool is_random);
```
Sets the global secret from `str` and stores its masked SHA-512 hash.
**Parameters:**
- `str` — secret bytes; may be plain text or Hex encoded. Whitespace is ignored during Hex decoding.
- `len` — length of `str` in bytes.
- `is_random` — set `true` if the secret is randomly generated.
If `str` is `NULL` or `len` is `0`, a random secret is generated and `is_random` is forced to `true`.
#### `fio_secret_set_at`
```c
SFUNC void fio_secret_set_at(fio_u512 *secret, char *str, size_t len);
```
Hashes `str` with SHA-512 and stores the masked result in `secret`.
**Parameters:**
- `secret` — destination hash. If `NULL`, the function returns silently.
- `str` — secret bytes; Hex decoding and whitespace handling apply as in `fio_secret_set`.
- `len` — length of `str`.
If `str` is `NULL` or `len` is `0`, random bytes are hashed instead.
#### `fio_secret_at`
```c
SFUNC fio_u512 fio_secret_at(fio_u512 *secret);
```
Unmasks a secret hash stored by `fio_secret_set_at`.
**Parameters:**
- `secret` — pointer to a masked `fio_u512`.
**Returns:** the unmasked SHA-512 hash.
### Example
```c
#define FIO_SECRET
#include "fio-stl.h"
int main(void) {
/* Set a custom secret and read it back. */
fio_secret_set("hunter2", 7, 0);
fio_u512 s = fio_secret();
/* s now holds SHA-512("hunter2"), masked while in global storage. */
fio_secure_zero(&s, sizeof(s));
return 0;
}
```
------------------------------------------------------------
# Brotli
```c
#define FIO_BROTLI
#include "fio-stl.h"
```
Brotli support for RFC 7932 data. The decoder handles the real format: Huffman tables, context modes, static dictionary, transforms, and the usual compressed-data acrobatics. The encoder provides practical quality levels 1–6.
## API
### `fio_brotli_decompress`
```c
SFUNC size_t fio_brotli_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses Brotli data.
Return values:
| Condition | Return value |
| --- | --- |
| Success | Decompressed byte count, `<= out_len`. |
| Output too small | Required buffer size, `> out_len`. |
| Corrupt input | `0`. |
| Size query | Required size, or `0` if corrupt / not knowable by scan. |
Pass `out == NULL` or `out_len == 0` to query the decompressed size. The query path scans meta-block lengths without allocating.
### `fio_brotli_decompress_bound`
```c
FIO_IFUNC size_t fio_brotli_decompress_bound(size_t in_len);
```
Returns a conservative decompression bound. The practical formula is `in_len * 1032 + 1024`, capped at 4GB for very large inputs. Prefer the size-query mode of `fio_brotli_decompress` when possible.
### `fio_brotli_compress`
```c
SFUNC size_t fio_brotli_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int quality);
```
Compresses data using Brotli. `quality` is clamped to 1–6:
- `1–2`: fast greedy matching.
- `3–4`: lazy matching.
- `5–6`: hash chain, context modeling, and static dictionary use.
Use an output buffer sized with `fio_brotli_compress_bound`. Returns compressed length on success, `0` on error.
### `fio_brotli_compress_bound`
```c
FIO_IFUNC size_t fio_brotli_compress_bound(size_t in_len);
```
Returns a generous compression bound: `in_len + (in_len >> 2) + 1024`.
## Example
```c
#define FIO_BROTLI
#include "fio-stl.h"
int roundtrip(const void *src, size_t src_len) {
size_t cap = fio_brotli_compress_bound(src_len);
uint8_t *compressed = malloc(cap);
if (!compressed)
return -1;
size_t compressed_len = fio_brotli_compress(compressed, cap, src, src_len, 5);
if (!compressed_len) {
free(compressed);
return -1;
}
size_t plain_len = fio_brotli_decompress(NULL, 0, compressed, compressed_len);
uint8_t *plain = malloc(plain_len);
if (!plain) {
free(compressed);
return -1;
}
size_t actual = fio_brotli_decompress(plain, plain_len, compressed, compressed_len);
free(plain);
free(compressed);
return actual == src_len ? 0 : -1;
}
```
## Implementation Notes
The decompressor uses a 64-bit LSB-first bit reader, two-level packed Huffman tables with an 8-bit first level, the RFC 7932 static dictionary, 121 transforms, and context-dependent literal decoding.
The compressor emits Brotli streams with bounded memory use and quality-dependent matching. For tiny input, simpler paths may win because being clever has overhead too.
------------------------------------------------------------
# DEFLATE / Gzip
```c
#define FIO_DEFLATE
#include "fio-stl.h"
```
Raw DEFLATE compression/decompression (RFC 1951), gzip wrappers (RFC 1952), and a streaming API for WebSocket `permessage-deflate` (RFC 7692).
This module is built around a 64-bit bit buffer, packed Huffman tables, a 32KB sliding window, and word-at-a-time match copies. Translation: it tries hard not to be slow.
## Raw DEFLATE
### `fio_deflate_decompress`
```c
SFUNC size_t fio_deflate_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses raw DEFLATE data with no zlib or gzip wrapper.
Return values:
| Condition | Return value |
| --- | --- |
| Success | Decompressed byte count, `<= out_len`. |
| Output too small | Required buffer size, `> out_len`. |
| Corrupt input | `0`. |
| Size query | Required size, or `0` if corrupt. |
Pass `out == NULL` or `out_len == 0` to run a decode pass that counts output bytes without writing them.
### `fio_deflate_decompress_bound`
```c
FIO_IFUNC size_t fio_deflate_decompress_bound(size_t in_len);
```
Returns a conservative decompression bound. For exact sizing, prefer the `fio_deflate_decompress(NULL, 0, ...)` query mode.
### `fio_deflate_compress`
```c
SFUNC size_t fio_deflate_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int level);
```
Compresses raw DEFLATE data.
Compression levels:
- `0`: store only.
- `1–3`: fast.
- `4–6`: normal.
- `7–9`: best compression.
Returns compressed length on success, `0` on error.
### `fio_deflate_compress_bound`
```c
FIO_IFUNC size_t fio_deflate_compress_bound(size_t in_len);
```
Returns an upper bound for raw DEFLATE output size.
## Gzip Wrappers
### `fio_gzip_compress`
```c
SFUNC size_t fio_gzip_compress(void *out,
size_t out_len,
const void *in,
size_t in_len,
int level);
```
Compresses with a gzip wrapper for uses such as HTTP `Content-Encoding: gzip`. The output includes the gzip header, DEFLATE payload, CRC32, and original size trailer.
Returns total output length, or `0` on error.
### `fio_gzip_decompress`
```c
SFUNC size_t fio_gzip_decompress(void *out,
size_t out_len,
const void *in,
size_t in_len);
```
Decompresses gzip data, including wrapper validation and CRC/size checks when data is actually decoded.
Return modes mirror `fio_deflate_decompress`: byte count, required size, or `0` on invalid input.
## Streaming API
### `fio_deflate_s`
```c
typedef struct fio_deflate_s fio_deflate_s;
```
Opaque streaming compression/decompression state (~32 bytes without
takeover). The streaming API supports two modes:
- **No-takeover (default, `fio_deflate_new`):** each flushed message is an
independent deflate stream. Compressor scratch (hash + token buffers)
comes from a contention-safe static slot pool (`FIO_STATIC_SAFE_ALLOC_DEF`)
checked out per call, so persistent per-context state is ~0. This is the
only mode the WebSocket layer negotiates (both `*_no_context_takeover`
flags are always forced).
- **Context takeover (`fio_deflate_new_takeover`):** matches may reference
the last 32KB of previous messages. The window (+ compressor hash) is
allocated inside the context's own block — the documented per-context cost
(~160KB compressor / ~32KB decompressor).
**Thread safety:** contexts are stateful and unsynchronized — use one
context per connection and serialize all `fio_deflate_push` calls per
context (one writer at a time). The static scratch pool is internally
synchronized; when every slot is momentarily busy, compression fails
gracefully (returns `0`).
**Overflow contract:** when the output buffer is too small, `fio_deflate_push`
(compression) and `fio_deflate_compress` return `0` — output is NEVER
silently truncated. Callers should treat `0` as "send uncompressed" or retry
with a correctly-sized buffer (`fio_deflate_compress_bound`, which is now
guaranteed sufficient at every level: the compressor falls back to stored
blocks whenever Huffman coding would expand, capping negative-gain output at
input + 5 bytes per 64KB block + a small header).
### `fio_deflate_new`
```c
SFUNC fio_deflate_s *fio_deflate_new(int level, int is_compress);
```
Creates a no-takeover streaming state. `is_compress != 0` creates a compressor; `0` creates a decompressor. Returns `NULL` on allocation failure. `level` is recorded for API compatibility; the streaming compressor always uses the fast greedy matcher (the one-shot `fio_deflate_compress` keeps levels 0-9).
### `fio_deflate_new_takeover`
```c
SFUNC fio_deflate_s *fio_deflate_new_takeover(int level, int is_compress);
```
Creates a streaming state with context takeover (cross-message history over the last 32KB). Allocates the window (+ compressor hash) inside the context's own block (~160KB compressor / ~32KB decompressor). `fio_deflate_destroy` resets the history (keeping the allocation).
### `fio_deflate_free`
```c
SFUNC void fio_deflate_free(fio_deflate_s *s);
```
Frees a streaming state.
### `fio_deflate_destroy`
```c
SFUNC void fio_deflate_destroy(fio_deflate_s *s);
```
Resets a streaming context. The input buffer is freed when it grew past 64KB, keeping persistent per-connection state bounded (no-takeover design).
### `fio_deflate_window_bits_set`
```c
SFUNC void fio_deflate_window_bits_set(fio_deflate_s *s, int bits);
```
Clamps compressor match distances to 2^`bits` (8..15, default 15). Used to honor `server_max_window_bits` from RFC 7692 negotiation. Decompression ignores it (any in-message distance up to 32KB is accepted).
### `fio_deflate_push`
```c
SFUNC size_t fio_deflate_push(fio_deflate_s *s,
void *out,
size_t out_len,
const void *in,
size_t in_len,
int flush);
```
Compresses or decompresses the next input chunk. Compression chunks input
into 32KB blocks (bounded scratch, no message-sized copies) and emits the
sync-flush trailer only on the message-final chunk.
- `flush == 0`: normal streaming (buffered up to 32KB, then auto-compressed).
- `flush == 1`: sync flush, useful at WebSocket frame boundaries.
For decompression, a return value greater than `out_len` means “retry with this much output space”; buffered input is preserved for that retry. Multi-block peer streams (e.g. zlib at any memLevel) inflate fully; the 9 completion bytes are appended internally.
## Example: Raw Roundtrip
```c
#define FIO_DEFLATE
#include "fio-stl.h"
int roundtrip(const void *src, size_t src_len) {
size_t cap = fio_deflate_compress_bound(src_len);
uint8_t *compressed = malloc(cap);
if (!compressed)
return -1;
size_t compressed_len = fio_deflate_compress(compressed, cap, src, src_len, 6);
if (!compressed_len) {
free(compressed);
return -1;
}
size_t plain_len = fio_deflate_decompress(NULL, 0, compressed, compressed_len);
uint8_t *plain = malloc(plain_len);
if (!plain) {
free(compressed);
return -1;
}
size_t actual = fio_deflate_decompress(plain, plain_len, compressed, compressed_len);
free(plain);
free(compressed);
return actual == src_len ? 0 : -1;
}
```
------------------------------------------------------------
# TLS 1.3 Standalone Library
```c
#define FIO_TLS13
#include "fio-stl.h"
```
Standalone TLS 1.3 crypto library — for IO-layer TLS see [`405 tls13.md`](https://facil.io/edge/native-tls-1-3-io-backend/).
This header implements TLS 1.3 building blocks and standalone client/server state machines: key schedule, record protection, handshake message helpers, alerts, KeyUpdate, certificate-request helpers, and client/server encrypt/decrypt flows.
It is not the facil.io IO integration layer. If you want TLS plugged into the evented IO stack, use the `405` layer. If you want the raw TLS 1.3 machinery, this is the sharp box.
**Security note:** this implementation has not been independently audited. Prefer audited platform TLS for production trust decisions when practical.
## Requirements
The key schedule needs HKDF, which needs SHA-2. The full TLS paths also depend on the selected AEAD, key exchange, certificate, and signature modules. `FIO_CRYPTO` is the easy button when you want the whole crypto set.
Common dependencies:
- `FIO_HKDF` / `FIO_SHA2` for key derivation.
- `FIO_AES` for AES-GCM suites.
- `FIO_CHACHA` for ChaCha20-Poly1305.
- `FIO_ED25519` for X25519 and Ed25519.
- `FIO_P256`, `FIO_P384`, `FIO_RSA`, `FIO_X509`, `FIO_PEM` as certificate/key support requires.
## Constants
```c
#define FIO_TLS13_SHA256_HASH_LEN 32
#define FIO_TLS13_SHA384_HASH_LEN 48
#define FIO_TLS13_MAX_HASH_LEN 48
#define FIO_TLS13_AES128_KEY_LEN 16
#define FIO_TLS13_AES256_KEY_LEN 32
#define FIO_TLS13_CHACHA_KEY_LEN 32
#define FIO_TLS13_IV_LEN 12
```
The record layer uses 12-byte IVs and 16-byte AEAD tags. Maximum TLS plaintext is 16 KiB.
## Main Types
The public types include:
- `fio_tls13_content_type_e` — alert, handshake, application data, and legacy change-cipher-spec content types.
- `fio_tls13_handshake_type_e` — ClientHello, ServerHello, Certificate, Finished, KeyUpdate, and related handshake message IDs.
- `fio_tls13_cipher_suite_e` — `TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, and `TLS_CHACHA20_POLY1305_SHA256`.
- `fio_tls13_cipher_type_e` — the AEAD implementation selector for record keys.
- `fio_tls13_record_keys_s` — per-direction write key, IV, sequence number, key length, and cipher type.
- Parsed handshake structs such as `fio_tls13_server_hello_s`, `fio_tls13_encrypted_extensions_s`, `fio_tls13_certificate_s`, `fio_tls13_certificate_verify_s`, and `fio_tls13_certificate_request_s`.
- `fio_tls13_client_s` and `fio_tls13_server_s` — standalone client/server TLS state.
## Key Schedule
These functions implement RFC 8446 section 7 derivation rules:
| Function | Purpose |
| --- | --- |
| `fio_tls13_hkdf_expand_label` | TLS-specific `HKDF-Expand-Label`. |
| `fio_tls13_derive_secret` | `Derive-Secret(secret, label, transcript_hash)`. |
| `fio_tls13_derive_early_secret` | Derive Early Secret from PSK or empty PSK. |
| `fio_tls13_derive_handshake_secret` | Derive Handshake Secret from Early Secret and ECDHE. |
| `fio_tls13_derive_master_secret` | Derive Master Secret. |
| `fio_tls13_derive_traffic_keys` | Derive write key and IV from a traffic secret. |
| `fio_tls13_derive_finished_key` | Derive the Finished MAC key. |
| `fio_tls13_compute_finished` | Compute Finished verify data. |
| `fio_tls13_update_traffic_secret` | Advance application traffic secret for KeyUpdate. |
`use_sha384 != 0` selects SHA-384; otherwise SHA-256 is used.
## Record Layer
| Function | Purpose |
| --- | --- |
| `fio_tls13_build_nonce` | XOR sequence number into IV to build the AEAD nonce. |
| `fio_tls13_record_keys_init` | Initialize `fio_tls13_record_keys_s`. |
| `fio_tls13_record_keys_clear` | Securely clear record keys. |
| `fio_tls13_record_encrypt` | Encrypt one TLS record and advance sequence number. |
| `fio_tls13_record_decrypt` | Decrypt one TLS record, recover inner content type, and advance sequence number. |
Record encryption appends the inner content type before AEAD protection. Decryption strips padding and reports the recovered `fio_tls13_content_type_e`.
## Handshake Helpers
| Function | Purpose |
| --- | --- |
| `fio_tls13_write_handshake_header` | Write a TLS handshake header. |
| `fio_tls13_build_client_hello` | Build a ClientHello with X25519 key share, SNI, and cipher suites. |
| `fio_tls13_parse_server_hello` | Parse ServerHello. |
| `fio_tls13_parse_encrypted_extensions` | Parse EncryptedExtensions. |
| `fio_tls13_parse_certificate` | Parse TLS Certificate handshake body. |
| `fio_tls13_parse_certificate_verify` | Parse CertificateVerify. |
| `fio_tls13_build_finished` | Build Finished handshake message from verify data. |
| `fio_tls13_parse_finished` | Verify a Finished message against expected verify data. |
| `fio_tls13_parse_certificate_request` | Parse CertificateRequest. |
| `fio_tls13_build_certificate_request` | Build CertificateRequest. |
## Alerts and KeyUpdate
| Function | Purpose |
| --- | --- |
| `fio_tls13_build_alert` | Build a plaintext alert payload. |
| `fio_tls13_send_alert` | Build and encrypt an alert record. |
| `fio_tls13_send_alert_plaintext` | Build a plaintext alert record. |
| `fio_tls13_build_key_update` | Build a KeyUpdate handshake message. |
| `fio_tls13_parse_key_update` | Parse KeyUpdate and extract request flag. |
| `fio_tls13_process_key_update` | Process peer KeyUpdate and rotate read/write state as needed. |
| `fio_tls13_send_key_update_response` | Send the required response update when requested. |
## Standalone Client API
| Function | Purpose |
| --- | --- |
| `fio_tls13_client_init` | Initialize client state for a server name. |
| `fio_tls13_client_destroy` | Clear client state. |
| `fio_tls13_client_set_trust_store` | Attach an X.509 trust store pointer. |
| `fio_tls13_client_skip_verification` | Enable/disable certificate verification skipping. Handle with tongs. |
| `fio_tls13_client_get_cert_error` | Return the last certificate validation error. |
| `fio_tls13_client_is_cert_verified` | Report certificate and chain verification status. |
| `fio_tls13_client_start` | Build the initial ClientHello. |
| `fio_tls13_client_process` | Process incoming handshake/record bytes and optionally emit output. |
| `fio_tls13_client_encrypt` | Encrypt application data. |
| `fio_tls13_client_decrypt` | Decrypt application data. |
| `fio_tls13_client_is_connected` | Test for connected state. |
| `fio_tls13_client_is_error` | Test for error state. |
| `fio_tls13_client_alpn_set` | Configure ALPN protocols. |
| `fio_tls13_client_set_cert` | Configure client certificate/private key. |
| `fio_tls13_client_set_public_key` | Configure client public key. |
| `fio_tls13_client_cert_requested` | Report whether the server requested a client certificate. |
## Standalone Server API
| Function | Purpose |
| --- | --- |
| `fio_tls13_server_init` | Initialize server state. |
| `fio_tls13_server_destroy` | Clear server state. |
| `fio_tls13_server_set_cert_chain` | Configure the server certificate chain. |
| `fio_tls13_server_set_private_key` | Configure the private key. |
| `fio_tls13_server_process` | Process incoming handshake/record bytes and optionally emit output. |
| `fio_tls13_server_encrypt` | Encrypt application data. |
| `fio_tls13_server_decrypt` | Decrypt application data. |
| `fio_tls13_server_is_connected` | Test for connected state. |
| `fio_tls13_server_is_error` | Test for error state. |
| `fio_tls13_server_alpn_set` | Configure ALPN protocols. |
| `fio_tls13_server_require_client_cert` | Configure client-certificate request/require mode. |
| `fio_tls13_server_client_cert_received` | Report whether a client certificate arrived. |
| `fio_tls13_server_client_cert_verified` | Report whether the client certificate verified. |
## Example Shape
```c
#define FIO_TLS13
#define FIO_CRYPTO
#include "fio-stl.h"
void client_start(uint8_t *out, size_t cap) {
fio_tls13_client_s client;
fio_tls13_client_init(&client, "example.com");
int len = fio_tls13_client_start(&client, out, cap);
if (len < 0) {
/* handle error */
}
fio_tls13_client_destroy(&client);
}
```
## Practical Notes
- `190 tls13.md` documents the standalone TLS 1.3 implementation.
- [`405 tls13.md`](https://facil.io/edge/native-tls-1-3-io-backend/) is the IO-layer integration point.
- Skipping certificate verification is for tests and controlled environments, not normal trust.
- Keep traffic secrets and record keys out of logs. They are not collectibles.
------------------------------------------------------------
# Dynamic Types — Module Group 200
The 200-range modules provide the core dynamic/generic types in the facil.io C STL: strings, arrays, maps, reference counting, and the FIOBJ soft-type system.
All five modules share the same design: **define a name macro, then include the header**. The preprocessor does the rest, generating a fully typed, prefixed API from static-inline functions and macros.
---
## The Pattern
```c
/* 1. Set the name (and any optional configuration macros). */
#define FIO_STR_NAME my_str
/* 2. Include the STL (single-header or multi-header). */
#include "fio-stl.h"
/* 3. Use the generated API — everything is prefixed with your name. */
my_str_s s = {0};
my_str_write(&s, "hello", 5);
my_str_destroy(&s);
```
Each inclusion is independent. You can define the same module multiple times in the same translation unit as long as you use a different name macro each time.
---
## Defining a Custom Type
| Name macro | What you get |
|---|---|
| `FIO_STR_NAME` | Binary-safe dynamic string type and API |
| `FIO_ARRAY_NAME` | Dynamic array for an element type you choose |
| `FIO_MAP_NAME` | Hash map or set (ordered or unordered) |
| `FIO_REF_NAME` | Reference-counted wrapper around any type |
| `FIO_FIOBJ` | Soft/dynamic object system (no extra name needed) |
Optional companion macros (e.g. `FIO_ARRAY_TYPE`, `FIO_MAP_KEY`, `FIO_MAP_VALUE`, `FIO_REF_TYPE`) let you configure element types, comparison functions, copy/destroy hooks, and more — consult the individual module docs for the full list.
---
## Modules
| Module | Name macro | Doc |
|---|---|---|
| Dynamic Strings | `FIO_STR_NAME` (or `FIO_STR_SMALL`) | [→ 201 string.md](./201 string.md) |
| Dynamic Arrays | `FIO_ARRAY_NAME` | [→ 202 array.md](./202 array.md) |
| Hash Maps / Sets | `FIO_MAP_NAME` | [→ 210 map.md](./210 map.md) |
| Reference Counting | `FIO_REF_NAME` | [→ 249 reference counter.md](./249 reference counter.md) |
| FIOBJ Soft Types | `FIO_FIOBJ` | [→ 250 fiobj.md](./250 fiobj.md) |
> **Note:** `210 map2.h` is a legacy file kept for backward compatibility and is not part of this active module group.
---
## Quick Example — Three Types, One File
```c
/* A small immutable-optimized string */
#define FIO_STR_SMALL tag_str
#include "fio-stl.h"
/* A dynamic array of those strings */
#define FIO_ARRAY_NAME tag_list
#define FIO_ARRAY_TYPE tag_str_s
#define FIO_ARRAY_TYPE_DESTROY(s) tag_str_destroy(&(s))
#include "fio-stl.h"
/* A map from string keys to integer values */
#define FIO_MAP_NAME word_count
#define FIO_MAP_KEY_KSTR /* use fio_keystr_s keys */
#define FIO_MAP_VALUE size_t
#include "fio-stl.h"
```
Each `#include` is processed independently. Define → include → use. That's it.
# Dynamic Strings — Module 201
Binary-safe dynamic strings, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_STR_NAME my_str /* required — sets the type and function prefix */
#include "fio-stl.h"
```
This generates `my_str_s` and a full API prefixed `my_str_*`.
Throughout this document the placeholder `STR` stands for whatever name you chose. All examples use `fio_str` as the name.
---
## Flavors
### Default — `FIO_STR_NAME`
The default struct stores capacity, length, and a buffer pointer (or embeds the string directly when it's short enough). On a 64-bit system the struct is 32 bytes, letting strings up to **30 bytes** live entirely inside the struct with no heap allocation.
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
```
Best for: strings that are written to multiple times, or strings in the 15–30 byte range that fit in the embedded slot.
### Small / immutable-optimized — `FIO_STR_SMALL`
```c
#define FIO_STR_SMALL key /* also sets FIO_STR_NAME = key */
#include "fio-stl.h"
```
Sets `FIO_STR_OPTIMIZE4IMMUTABILITY 1`, which drops the capacity field to shrink the struct to 16 bytes. Short strings (≤ 14 bytes on 64-bit) are still embedded. Editing long strings is slower because capacity must be recalculated on each write.
Best for: map keys, mostly-immutable strings, or any context where struct size matters more than write throughput.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_STR_NAME`:
| Macro | Default | Effect |
|---|---|---|
| `FIO_STR_OPTIMIZE_EMBEDDED` | `0` | Each unit adds `sizeof(char*)` to the struct, growing the embedded-string capacity. Max 4 (default) or 1 (`FIO_STR_SMALL`). |
| `FIO_STR_OPTIMIZE4IMMUTABILITY` | `0` | Drops the capacity field; set automatically by `FIO_STR_SMALL`. |
---
## Initialization Macros
| Macro | Notes |
|---|---|
| `FIO_STR_INIT` | Zero-initializes the struct. Use for stack or heap allocation. |
| `FIO_STR_INIT_EXISTING(buf, len, capa)` | Wraps an already-allocated buffer. **Not valid for `FIO_STR_SMALL`**. |
| `FIO_STR_INIT_STATIC(cstr)` | Wraps a string literal; `free` won't touch the buffer. **Not valid for `FIO_STR_SMALL`**. |
| `FIO_STR_INIT_STATIC2(buf, len)` | Same as above with explicit length. **Not valid for `FIO_STR_SMALL`**. |
```c
fio_str_s s = FIO_STR_INIT; /* stack, zero-initialized */
fio_str_s *p = malloc(sizeof(*p));
*p = (fio_str_s)FIO_STR_INIT; /* heap, zero-initialized */
fio_str_s ro = FIO_STR_INIT_STATIC("read-only literal");
```
---
## Lifecycle
### Heap allocation
```c
fio_str_s *STR_new(void); /* allocate + zero-initialize */
void STR_free(fio_str_s *s); /* destroy content + free struct */
```
### Stack / embedded destruction
```c
void STR_destroy(fio_str_s *s); /* free content, re-initialize struct */
```
### Initialization helpers
```c
/* Wrap a static/const buffer. Copies if it fits embedded, otherwise borrows. */
fio_str_info_s STR_init_const(fio_str_s *s, const char *str, size_t len);
/* Always copies src into s. */
fio_str_info_s STR_init_copy(fio_str_s *s, const char *str, size_t len);
/* Copies another STR object into dest. */
fio_str_info_s STR_init_copy2(fio_str_s *dest, fio_str_s *src);
```
### Detach / hand-off
```c
/* Remove string data from s, return a heap-allocated C string. s is reset. */
char *STR_detach(fio_str_s *s);
/* Free a pointer returned by STR_detach. */
void STR_dealloc(void *ptr);
```
`detach` always returns a dynamically allocated buffer (never embedded data). Free it with `STR_dealloc`, not plain `free`, unless you know the allocator matches.
---
## State Inspection
```c
fio_str_info_s STR_info(const fio_str_s *s); /* buf + len + capa */
fio_buf_info_s STR_buf (const fio_str_s *s); /* buf + len only */
char *STR_ptr (fio_str_s *s); /* char * to data */
size_t STR_len (fio_str_s *s); /* byte length */
size_t STR_capa(fio_str_s *s); /* allocated capacity */
```
`fio_str_info_s` is a plain struct: `{ size_t len; char *buf; size_t capa; }`. When `capa == 0` the buffer is read-only (frozen or static). Data is always NUL-terminated but may contain embedded NUL bytes — don't use `strlen` on it.
```c
void STR_freeze (fio_str_s *s); /* make read-only */
uint8_t STR_is_frozen (fio_str_s *s); /* 1 if frozen */
int STR_is_allocated(const fio_str_s *s); /* 1 if heap buffer in use */
int STR_is_eq (const fio_str_s *a,
const fio_str_s *b); /* binary equality */
uint64_t STR_hash (const fio_str_s *s,
uint64_t seed); /* Risky Hash */
```
---
## Memory Management
```c
/* Resize without reallocation (capped by current capacity).
Returns updated state. Shrinking may lose data beyond the new length. */
fio_str_info_s STR_resize(fio_str_s *s, size_t size);
/* Try to release unused capacity. Effect depends on allocator. */
void STR_compact(fio_str_s *s);
/* Reserve at least `amount` additional bytes beyond current length.
NOT available for FIO_STR_SMALL types. */
fio_str_info_s STR_reserve(fio_str_s *s, size_t amount);
```
`reserve` + manual write + `resize` is the fastest pattern for bulk construction:
```c
fio_str_s s = FIO_STR_INIT;
fio_str_info_s info = fio_str_reserve(&s, 128);
size_t written = my_encoder(info.buf + fio_str_len(&s), 128, src);
fio_str_resize(&s, fio_str_len(&s) + written);
```
---
## Write / Append
All write functions append to the **end** of the string and return the updated `fio_str_info_s`.
```c
fio_str_info_s STR_write(fio_str_s *s, const void *src, size_t src_len);
fio_str_info_s STR_concat(fio_str_s *dest, fio_str_s *src); /* alias: STR_join */
fio_str_info_s STR_replace(fio_str_s *s,
intptr_t start_pos, /* negative = from end */
size_t old_len, /* 0 = insert only */
const void *src,
size_t src_len); /* 0 = erase only */
```
### Multi-write macro
```c
FIO_STR_WRITE2(str_name, dest_ptr, ...fio_string_write_s items...);
```
Combines multiple writes in one call using `fio_string_write_s` descriptors:
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
fio_str_s s = FIO_STR_INIT;
FIO_STR_WRITE2(fio_str, &s,
FIO_STRING_WRITE_STR1("Hello "),
FIO_STRING_WRITE_STR1("World!"));
/* s now contains "Hello World!" */
fio_str_destroy(&s);
```
---
## Number Writing
```c
fio_str_info_s STR_write_i (fio_str_s *s, int64_t num); /* decimal */
fio_str_info_s STR_write_hex(fio_str_s *s, int64_t num); /* raw hex digits, no 0x prefix */
fio_str_info_s STR_write_bin(fio_str_s *s, int64_t num); /* binary repr */
```
---
## Formatted Writes
```c
fio_str_info_s STR_printf (fio_str_s *s, const char *fmt, ...);
fio_str_info_s STR_vprintf(fio_str_s *s, const char *fmt, va_list argv);
```
Both append to the end of `s`.
```c
fio_str_s msg = FIO_STR_INIT;
fio_str_printf(&msg, "items: %zu, rate: %.2f\n", count, rate);
puts(fio_str_ptr(&msg));
fio_str_destroy(&msg);
```
---
## UTF-8
```c
size_t STR_utf8_valid (fio_str_s *s); /* char count if valid, 0 if not */
size_t STR_utf8_len (fio_str_s *s); /* character count */
int STR_utf8_select(fio_str_s *s, intptr_t *pos, size_t *len); /* char→byte slice */
```
`utf8_select` converts a UTF-8 character position and length to byte offsets in-place. Sets `*pos = -1` on invalid UTF-8 before the selection point. Returns -1 on error, 0 on success.
---
## Escaping
### JSON / C escaping
```c
fio_str_info_s STR_write_escape (fio_str_s *s, const void *data, size_t len);
fio_str_info_s STR_write_unescape(fio_str_s *s, const void *escaped, size_t len);
```
### HTML escaping
```c
fio_str_info_s STR_write_html_escape (fio_str_s *s, const void *raw, size_t len);
fio_str_info_s STR_write_html_unescape(fio_str_s *s, const void *escaped, size_t len);
```
`write_html_unescape` handles numeric code-points and a minimal set of named entities — it is intentionally incomplete.
---
## Base64
```c
fio_str_info_s STR_write_base64enc(fio_str_s *s,
const void *data, size_t data_len,
uint8_t url_encoded); /* 1 for URL-safe variant */
fio_str_info_s STR_write_base64dec(fio_str_s *s,
const void *encoded, size_t encoded_len);
```
---
## File / FD Reading
```c
/* Append from open file descriptor (regular files only; fd stays open). */
fio_str_info_s STR_readfd(fio_str_s *s, int fd,
intptr_t start_at, intptr_t limit);
/* Open filename, append slice, close. limit==0 reads to EOF. */
fio_str_info_s STR_readfile(fio_str_s *s, const char *filename,
intptr_t start_at, intptr_t limit);
```
On failure `STR_readfile` returns a state with `buf == NULL`.
---
## Quick Example
```c
#define FIO_STR_NAME fio_str
#include "fio-stl.h"
void greet(void) {
fio_str_s s = FIO_STR_INIT;
fio_str_write(&s, "Hello", 5);
fio_str_write(&s, ", ", 2);
fio_str_write(&s, "world!", 6);
printf("%.*s\n", (int)fio_str_len(&s), fio_str_ptr(&s));
/* → Hello, world! */
fio_str_replace(&s, 0, 5, "Goodbye", 7);
printf("%.*s\n", (int)fio_str_len(&s), fio_str_ptr(&s));
/* → Goodbye, world! */
fio_str_destroy(&s);
}
```
### Heap-allocated with reference counting
```c
#define FIO_STR_NAME fio_str
#define FIO_REF_NAME fio_str
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_DESTROY(s) fio_str_destroy(&(s))
#include "fio-stl.h"
void example(void) {
fio_str_s *s = fio_str_new();
fio_str_write(s, "shared", 6);
fio_str_s *ref = fio_str_dup(s); /* increment ref count */
fio_str_free(ref); /* decrement; destroys string at zero */
fio_str_free(s);
}
```
---
## Embedded-string Sizes (64-bit reference)
| Variant | Struct size | Max embedded string |
|---|---|---|
| `FIO_STR_NAME` (default) | 32 bytes | 30 bytes |
| `FIO_STR_NAME` + `FIO_STR_OPTIMIZE_EMBEDDED 1` | 40 bytes | 38 bytes |
| `FIO_STR_SMALL` | 16 bytes | 14 bytes |
Two bytes are always reserved for the metadata byte and the NUL terminator.
---
See [→ 200 types-overview.md](./200 types-overview.md) for the shared define-include-use pattern and how to combine this type with arrays, maps, and reference counters.
# Dynamic Arrays — Module 202
Type-safe dynamic arrays, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_ARRAY_NAME ary /* required — sets the type and function prefix */
#define FIO_ARRAY_TYPE int /* optional — element type; default void * */
#include "fio-stl.h"
```
This generates `ary_s` and a full API prefixed `ary_*`.
Throughout this document `ARY` stands for whatever name you chose. `ARY_PTR` is shorthand for `FIO_ARRAY_PTR` (usually `ARY_s *`). All examples use `fio_ary` as the name.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_ARRAY_NAME`.
### Element type
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_TYPE` | `void *` | The element type stored in the array. |
| `FIO_ARRAY_TYPE_INVALID` | `NULL` / `{0}` | Sentinel value returned for out-of-bounds access. |
| `FIO_ARRAY_TYPE_INVALID_SIMPLE` | `1` if `{0}` | Set to `1` if the invalid element is all-zero bytes. |
| `FIO_ARRAY_TYPE_STR` | — | Shortcut: configures the array for `fio_keystr_s` string elements (sets type, copy, destroy, and compare automatically). |
### Element hooks
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_TYPE_COPY(dest, src)` | `dest = src` | Called when copying an element into the array. |
| `FIO_ARRAY_TYPE_DESTROY(obj)` | _(nothing)_ | Called when an element is removed or the array is destroyed. |
| `FIO_ARRAY_TYPE_CMP(a, b)` | `a == b` | Called by `find`, `remove2`, etc. |
| `FIO_ARRAY_TYPE_CONCAT_COPY(dest, src)` | same as `COPY` | Used during `concat`; override if concat needs different copy semantics. |
| `FIO_ARRAY_DESTROY_AFTER_COPY` | `1` if both hooks are non-trivial | When set, `DESTROY` is called on an element after it has been copied to an `old` pointer. |
### Memory / growth
| Macro | Default | Effect |
|---|---|---|
| `FIO_ARRAY_PADDING` | `4` | Extra empty slots allocated beyond the requested capacity. |
| `FIO_ARRAY_EXPONENTIAL` | `0` | Set to `1` for exponential (doubling) growth. Linear growth with padding is the default. |
| `FIO_ARRAY_ENABLE_EMBEDDED` | `1` | Store small arrays directly inside the struct (no heap allocation). Values > 1 add extra struct fields to increase the embedded capacity. |
---
## Initialization
```c
ARY_s a = FIO_ARRAY_INIT; /* stack — zero-initialize */
ARY_s *p = ARY_new(); /* heap — allocate + zero-initialize */
```
`FIO_ARRAY_INIT` expands to `{0}`, so a zero-initializing `memset` also works.
---
## Lifecycle
```c
ARY_s *ARY_new(void); /* allocate + initialize on the heap */
void ARY_free(ARY_PTR ary); /* destroy elements + free the container */
void ARY_destroy(ARY_PTR ary); /* destroy elements, reset to empty (stack-safe) */
```
Use `ARY_destroy` for stack-allocated arrays. Use `ARY_new` / `ARY_free` for heap.
```c
#define FIO_ARRAY_NAME fio_ary
#define FIO_ARRAY_TYPE int
#include "fio-stl.h"
void example(void) {
fio_ary_s a = FIO_ARRAY_INIT;
fio_ary_push(&a, 1);
fio_ary_push(&a, 2);
fio_ary_push(&a, 3);
/* ... use ... */
fio_ary_destroy(&a);
}
```
---
## State Inspection
```c
uint32_t ARY_count(ARY_PTR ary); /* number of elements */
uint32_t ARY_capa (ARY_PTR ary); /* current capacity */
int ARY_is_embedded(ARY_PTR ary); /* 1 = embedded, 0 = heap alloc, -1 = error */
FIO_ARRAY_TYPE *ARY2ptr(ARY_PTR ary); /* raw C pointer to the first element */
```
`ARY_is_embedded` reports whether the array currently uses in-struct storage. `ARY2ptr` returns a pointer directly into the backing array. It is valid until the next mutating call. Useful for bulk reads or passing to functions that expect a C array.
---
## Memory Management
```c
/* Reserve capacity for at least `capa` additional elements.
Negative capa reserves space at the beginning of the array.
Returns the new capacity. */
uint32_t ARY_reserve(ARY_PTR ary, int64_t capa);
/* Attempt to shrink memory to fit the current element count. */
void ARY_compact(ARY_PTR ary);
```
Growth is linear by default (current count + padding). Exponential growth can be enabled with `FIO_ARRAY_EXPONENTIAL 1`. Always call `ARY_reserve` before large batch inserts to avoid repeated reallocation.
---
## Push / Pop (end of array)
```c
/* Append an element. Returns a pointer to the new element, or NULL on error. */
FIO_ARRAY_TYPE *ARY_push(ARY_PTR ary, FIO_ARRAY_TYPE data);
/* Remove the last element.
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 if the array is empty. */
int ARY_pop(ARY_PTR ary, FIO_ARRAY_TYPE *old);
```
```c
fio_ary_push(&a, 42);
int out;
if (!fio_ary_pop(&a, &out))
printf("popped: %d\n", out);
```
---
## Unshift / Shift (beginning of array)
```c
/* Prepend an element. Returns a pointer to the new element, or NULL on error.
May trigger memmove. */
FIO_ARRAY_TYPE *ARY_unshift(ARY_PTR ary, FIO_ARRAY_TYPE data);
/* Remove the first element.
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 if the array is empty. */
int ARY_shift(ARY_PTR ary, FIO_ARRAY_TYPE *old);
```
Both `push`/`pop` and heap-backed `unshift`/`shift` are O(1) amortized. The implementation maintains headroom at both ends of the heap buffer to keep prepend operations cheap; the tiny embedded path may move elements.
---
## Random Access
```c
/* Set the element at `index`. Grows the array if needed, filling gaps with
FIO_ARRAY_TYPE_INVALID.
Negative index counts from the end (-1 == last element).
If `old` is not NULL, the previous value is copied there before being
destroyed.
Returns a pointer to the set element, or NULL on error. */
FIO_ARRAY_TYPE *ARY_set(ARY_PTR ary, int64_t index, FIO_ARRAY_TYPE data,
FIO_ARRAY_TYPE *old);
/* Return the element at `index` (no copy performed).
Negative index counts from the end.
Returns FIO_ARRAY_TYPE_INVALID if out of bounds. */
FIO_ARRAY_TYPE ARY_get(ARY_PTR ary, int64_t index);
```
```c
fio_ary_set(&a, 5, 99, NULL); /* extend + set index 5 */
int v = fio_ary_get(&a, -1); /* last element */
int prev;
fio_ary_set(&a, 0, 7, &prev); /* replace index 0, capture old value */
```
---
## Concat
```c
/* Append all elements from `src` to `dest`.
`src` is not modified.
Returns `dest`, or NULL on allocation failure. */
ARY_PTR ARY_concat(ARY_PTR dest, ARY_PTR src);
```
Uses `FIO_ARRAY_TYPE_CONCAT_COPY` (defaults to `FIO_ARRAY_TYPE_COPY`) for each element, so managed types are properly duplicated.
---
## Search and Removal
```c
/* Find the first occurrence of `data` starting at `start_at`.
Negative `start_at` seeks backwards (-1 == last element).
Returns the index, or FIO_ARRAY_NOT_FOUND ((uint32_t)-1). */
uint32_t ARY_find(ARY_PTR ary, FIO_ARRAY_TYPE data, int64_t start_at);
/* Remove element at `index`. All subsequent elements shift left (memmove).
If `old` is not NULL, the removed value is copied there.
Returns 0 on success, -1 on error. O(n). */
int ARY_remove(ARY_PTR ary, int64_t index, FIO_ARRAY_TYPE *old);
/* Remove all occurrences of `data`. Elements are compacted in-place.
Returns the count of removed items. O(n). */
uint32_t ARY_remove2(ARY_PTR ary, FIO_ARRAY_TYPE data);
```
```c
#define FIO_ARRAY_NOT_FOUND ((uint32_t)-1)
uint32_t idx = fio_ary_find(&a, 42, 0);
if (idx != FIO_ARRAY_NOT_FOUND)
fio_ary_remove(&a, idx, NULL);
```
---
## Iteration
### Callback-based
```c
/** Iteration info passed to the callback. */
typedef struct ARY_each_s {
ARY_PTR const parent; /* the array being iterated */
uint64_t index; /* current element index */
int (*task)(struct ARY_each_s *info); /* the callback; may be updated mid-cycle */
void *udata; /* opaque user data */
FIO_ARRAY_TYPE value; /* the current element's value */
uint64_t padding; /* reserved */
} ARY_each_s;
/* Call `task` for each element starting at `start_at`.
Returning -1 from `task` stops the loop.
Returns the stop position (elements processed + start). */
uint32_t ARY_each(ARY_PTR ary,
int (*task)(ARY_each_s *info),
void *udata,
int64_t start_at);
```
### Pointer iteration
```c
/* Returns a pointer to the next element.
Pass NULL for `pos` to get the first element.
`first` tracks the base pointer across reallocations.
Returns NULL when the end is reached. */
FIO_ARRAY_TYPE *ARY_each_next(ARY_PTR ary,
FIO_ARRAY_TYPE **first,
FIO_ARRAY_TYPE *pos);
```
### Loop macro
```c
/* Iterates with a typed `pos` pointer.
`array_name` must be the literal macro name used when generating the type.
Avoid structural mutations inside the loop (push/pop/remove may invalidate pos). */
FIO_ARRAY_EACH(array_name, array_ptr, pos) { /* pos is a FIO_ARRAY_TYPE * */ }
```
```c
#define FIO_ARRAY_NAME fio_ary
#define FIO_ARRAY_TYPE int
#include "fio-stl.h"
void print_all(fio_ary_s *a) {
FIO_ARRAY_EACH(fio_ary, a, pos) {
printf("%d\n", *pos);
}
}
```
---
## Embedded Arrays
When `FIO_ARRAY_ENABLE_EMBEDDED` is set (the default), small arrays are stored directly inside the `ARY_s` struct, skipping heap allocation. For `void *` arrays on a 64-bit system, the struct has room for 2 pointers embedded.
Setting `FIO_ARRAY_ENABLE_EMBEDDED` to a value greater than 1 adds extra fields to the struct, increasing embedded capacity at the cost of a larger struct.
`ARY_is_embedded(ary)` returns `1` when the array is currently in embedded mode. `ARY_compact` will re-embed an array if its count fits within the embedded capacity.
---
## Practical Example — String Pointer Array
```c
#define FIO_ARRAY_NAME str_list
#define FIO_ARRAY_TYPE const char *
#include "fio-stl.h"
void example(void) {
str_list_s list = FIO_ARRAY_INIT;
str_list_push(&list, "apple");
str_list_push(&list, "banana");
str_list_push(&list, "cherry");
FIO_ARRAY_EACH(str_list, &list, pos) {
printf("%s\n", *pos);
}
str_list_destroy(&list);
}
```
---
## Practical Example — Struct Array with Custom Hooks
```c
typedef struct { int id; float val; } point_s;
#define FIO_ARRAY_NAME pt_ary
#define FIO_ARRAY_TYPE point_s
#define FIO_ARRAY_TYPE_CMP(a,b) ((a).id == (b).id)
#include "fio-stl.h"
void example(void) {
pt_ary_s a = FIO_ARRAY_INIT;
pt_ary_push(&a, (point_s){.id = 1, .val = 1.5f});
pt_ary_push(&a, (point_s){.id = 2, .val = 2.5f});
point_s key = {.id = 1};
uint32_t idx = pt_ary_find(&a, key, 0);
if (idx != FIO_ARRAY_NOT_FOUND)
printf("found at %u\n", idx);
pt_ary_destroy(&a);
}
```
---
## Performance Notes
- **Access / set**: O(1).
- **Iteration**: excellent cache locality.
- **push / pop**: O(1) amortized.
- **unshift / shift**: O(1) amortized for heap-backed arrays; the tiny embedded path may move elements.
- **find**: O(n) worst case.
- **remove / remove2**: O(n) — involve `memmove`.
- Growth is **linear** by default (count + `FIO_ARRAY_PADDING`). Use `FIO_ARRAY_EXPONENTIAL 1` or call `ARY_reserve` up-front to avoid repeated reallocations on large batch inserts.
- Capacity is limited to **32 bits** (max ~4 billion elements).
---
See [→ 200 types-overview.md](./200 types-overview.md) for the shared define-include-use pattern and how to combine arrays with strings, maps, and reference counters.
# Hash Maps and Sets — Module 210
Type-safe hash maps and sets, generated from a name macro. One header covers both unordered and ordered (FIFO/LRU) variants.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_MAP_NAME my_map /* required — sets the type and function prefix */
#define FIO_MAP_KEY uint64_t /* optional — key type; default is fio_str_info_s (bstr) */
#define FIO_MAP_VALUE void * /* optional — omit for a Set instead of a Map */
#include "fio-stl.h"
```
This generates `my_map_s` and a full API prefixed `my_map_*`.
Throughout this document `MAP` stands for whatever name you chose. All examples use `map` as the name.
---
## Sets vs. Maps
- **Set**: define only `FIO_MAP_NAME` (and optionally `FIO_MAP_KEY`). Each element is the key itself.
- **Hash Map**: also define `FIO_MAP_VALUE`. Elements are key→value pairs.
If `FIO_MAP_KEY` is left undefined (or `FIO_MAP_KEY_BSTR` is defined), keys default to binary strings via `fio_str_info_s` / `fio_bstr`.
---
## Configuration Macros
Set these **before** `#include`, after `FIO_MAP_NAME`.
### Naming shortcuts
| Macro | Effect |
|---|---|
| `FIO_MAP_NAME name` | Required. Sets the type and function prefix. |
| `FIO_OMAP_NAME name` | Shortcut: defines `FIO_MAP_NAME` and forces `FIO_MAP_ORDERED 1`. |
| `FIO_UMAP_NAME name` | Shortcut: defines `FIO_MAP_NAME` and forces `FIO_MAP_ORDERED 0`. |
### Key configuration
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_KEY` | `fio_str_info_s` | External (API) key type. |
| `FIO_MAP_KEY_BSTR` | — | Shortcut: API keys are `fio_str_info_s`; internally stored as `fio_bstr` (heap-allocated, NUL-terminated). This is the default when `FIO_MAP_KEY` is not defined. |
| `FIO_MAP_KEY_KSTR` | — | Shortcut: API keys are `fio_str_info_s`; internally stored as `fio_keystr_s` with small-string optimisation (≤14 bytes inline on 64-bit). Better cache locality than `bstr`. |
| `FIO_MAP_KEY_INTERNAL` | `FIO_MAP_KEY` | Internal storage type (if different from the API type). |
| `FIO_MAP_KEY_FROM_INTERNAL(k)` | `k` | Converts internal storage → API type. |
| `FIO_MAP_KEY_COPY(dest, src)` | `(dest) = (src)` | Copies an API key into internal storage. |
| `FIO_MAP_KEY_CMP(a, b)` | `(a) == (b)` | Compares an internal key with an API key. |
| `FIO_MAP_KEY_DESTROY(key)` | _(nothing)_ | Destroys a key in internal storage (free resources). |
| `FIO_MAP_KEY_DISCARD(key)` | _(nothing)_ | Destroys an API key that was not inserted (e.g. if you pre-allocated or ref-counted it). |
### Value configuration
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_VALUE` | _(none — Set)_ | External (API) value type. Defining this makes the template a hash map. |
| `FIO_MAP_VALUE_BSTR` | — | Shortcut: binary-string values via `fio_bstr`. |
| `FIO_MAP_VALUE_INTERNAL` | `FIO_MAP_VALUE` | Internal storage type. |
| `FIO_MAP_VALUE_FROM_INTERNAL(v)` | `v` | Converts internal storage → API type. |
| `FIO_MAP_VALUE_COPY(dest, src)` | `(dest) = (src)` | Copies an API value into internal storage. |
| `FIO_MAP_VALUE_DESTROY(v)` | _(nothing)_ | Destroys a value in internal storage. |
| `FIO_MAP_VALUE_DISCARD(v)` | _(nothing)_ | Destroys an API value that was not stored. |
### Hashing
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_HASH_FN(key)` | _(not defined)_ | When defined, the map computes its own hash from the API key. Callers no longer pass a `hash` argument. |
| `FIO_MAP_RECALC_HASH` | `0` | Set to `1` to skip caching the hash per-node (saves 8 bytes/node). Requires `FIO_MAP_HASH_FN`. |
When `FIO_MAP_HASH_FN` is **not** defined, every `get`/`set`/`remove` call takes an explicit `uint64_t hash` argument — letting you salt the hash per-map for defence against hash-flooding.
### Ordering
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_ORDERED` | `0` | Set to `1` (or define without a value) for insertion-order (FIFO) iteration and `evict`. Adds 8 bytes per node. |
| `FIO_MAP_LRU` | _(not defined)_ | Implies `FIO_MAP_ORDERED 1` and keeps LRU-style iteration order. The value is ignored. Does **not** auto-evict; call `MAP_evict()` yourself to enforce a size limit. |
### Limits
| Macro | Default | Effect |
|---|---|---|
| `FIO_MAP_ATTACK_LIMIT` | `16` | Max full hash collisions before the map assumes a hash-flooding attack and starts overwriting stale entries. |
| `FIO_MAP_CAPA_BITS_LIMIT` | `31` | Max exponent for internal capacity (2^31 ≈ 2 billion elements). Cannot exceed 31 without rewriting internal code. |
---
## Initialization
```c
MAP_s m = FIO_MAP_INIT; /* stack — zero-initialize */
MAP_s *p = MAP_new(); /* heap — allocate + zero-initialize */
```
`FIO_MAP_INIT` expands to `{0}`.
---
## Lifecycle
```c
MAP_s *MAP_new(void); /* allocate + initialize on the heap */
void MAP_free(MAP_PTR map); /* destroy elements + free the container */
void MAP_destroy(MAP_PTR map); /* destroy elements, reset to empty (stack-safe) */
```
Use `MAP_destroy` for stack-allocated maps. Use `MAP_new` / `MAP_free` for heap.
---
## State
```c
uint32_t MAP_count(MAP_PTR map); /* number of stored elements */
uint32_t MAP_capa (MAP_PTR map); /* theoretical capacity (always a power of 2) */
void MAP_reserve(MAP_PTR map, size_t capa); /* ensure at least capa slots exist */
void MAP_compact(MAP_PTR map); /* shrink allocation to fit current count */
```
---
## Get, Set, Remove
The exact signature depends on whether `FIO_MAP_HASH_FN` is defined and whether `FIO_MAP_VALUE` is defined. Below `[hash,]` means the `uint64_t hash` argument is present only when `FIO_MAP_HASH_FN` is **not** defined.
```c
/* Returns the stored value (or key for a Set). Zero-value if not found. */
MAP_GET_T MAP_get(MAP_PTR map, [uint64_t hash,] MAP_KEY key);
/* Insert or overwrite. Returns the resulting stored value/key.
For hash maps: `old` (optional) receives the previous value before it is destroyed. */
MAP_GET_T MAP_set(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val, MAP_VALUE_INTERNAL *old]);
/* Insert only if the key is absent. Returns current value/key. */
MAP_GET_T MAP_set_if_missing(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val]);
/* Remove. Returns 0 on success, -1 if not found.
`old` (optional): receives the removed value/key before it is destroyed. */
/* For hash maps: */
int MAP_remove(MAP_PTR map, [uint64_t hash,] MAP_KEY key,
MAP_VALUE_INTERNAL *old);
/* For sets: */
int MAP_remove(MAP_PTR map, [uint64_t hash,] MAP_KEY key,
MAP_KEY_INTERNAL *old);
/* Evict `n` elements. Order depends on FIO_MAP_LRU / FIO_MAP_ORDERED. */
void MAP_evict(MAP_PTR map, size_t n);
/* Remove all elements without freeing the internal buffer. */
void MAP_clear(MAP_PTR map);
```
---
## Low-Level Node Access
`MAP_get_ptr` and `MAP_set_ptr` return a pointer into internal storage. Use when you need to mutate a value in place or avoid a copy.
```c
/* Returns internal node pointer, or NULL if missing. */
MAP_node_s *MAP_get_ptr(MAP_PTR map, [uint64_t hash,] MAP_KEY key);
/* Core insert/overwrite. `overwrite=1` replaces an existing value; `overwrite=0` keeps it.
Returns internal node pointer, or NULL on error. */
MAP_node_s *MAP_set_ptr(MAP_PTR map, [uint64_t hash,]
MAP_KEY key [, MAP_VALUE val,
MAP_VALUE_INTERNAL *old, int overwrite]);
```
Accessors for node pointers:
```c
MAP_KEY MAP_node2key (MAP_node_s *node); /* external key */
MAP_VALUE MAP_node2val (MAP_node_s *node); /* external value (or key for Sets) */
uint64_t MAP_node2hash (MAP_node_s *node); /* hash value */
MAP_KEY_INTERNAL *MAP_node2key_ptr(MAP_node_s *node); /* pointer to internal key */
MAP_VALUE_INTERNAL*MAP_node2val_ptr(MAP_node_s *node); /* pointer to internal value */
```
All return a zeroed/NULL result when `node` is NULL.
---
## Iteration
### `FIO_MAP_EACH` / `FIO_MAP_EACH_REVERSED` — for-loop macros (preferred)
```c
FIO_MAP_EACH(map_name, map_ptr, i) {
/* i.key — current key (API type) */
/* i.value — current value (API type; absent for Sets) */
/* i.hash — current hash (absent if FIO_MAP_RECALC_HASH is set) */
}
FIO_MAP_EACH_REVERSED(map_name, map_ptr, i) { /* same, backward */ }
```
`i` is declared inside the loop as a `MAP_iterator_s`.
**Example:**
```c
#define FIO_MAP_NAME wcount
#define FIO_MAP_KEY_KSTR /* fio_str_info_s API keys, fio_keystr_s storage */
#define FIO_MAP_VALUE size_t
#define FIO_MAP_HASH_FN(k) fio_risky_hash((k).buf, (k).len, (uintptr_t)wcount_destroy)
#include "fio-stl.h"
void print_counts(wcount_s *m) {
FIO_MAP_EACH(wcount, m, it)
printf("%.*s => %zu\n", (int)it.key.len, it.key.buf, it.value);
}
```
### Manual iterator API
```c
/* Returns the first iterator (NULL current_pos) or the next one. */
MAP_iterator_s MAP_get_next(MAP_PTR map, MAP_iterator_s *current_pos);
/* Reverse: returns the last (NULL) or the previous one. */
MAP_iterator_s MAP_get_prev(MAP_PTR map, MAP_iterator_s *current_pos);
/* Returns 1 if valid, 0 if exhausted or invalidated. */
int MAP_iterator_is_valid(MAP_iterator_s *iter);
/* Returns the internal node pointer for an iterator. */
MAP_node_s *MAP_iterator2node(MAP_PTR map, MAP_iterator_s *iter);
```
Modifying the map (insert, rehash) between iterator steps is safe but may invalidate or reorder iterators for unordered maps.
### Callback API
```c
/* Calls task(info) for each element starting at `start_at`.
Returning -1 from the callback stops the loop.
Returns the final position (items processed + start_at). */
uint32_t MAP_each(MAP_PTR map,
int (*task)(MAP_each_s *info),
void *udata,
ssize_t start_at);
```
The callback receives a `MAP_each_s` pointer with:
```c
typedef struct {
MAP_PTR const parent; /* the map being iterated */
uint64_t index; /* current element index */
int (*task)(MAP_each_s *); /* the callback (may be swapped mid-loop) */
void *udata; /* opaque user data */
MAP_VALUE value; /* current value (absent for Sets) */
MAP_KEY key; /* current key */
} MAP_each_s;
```
---
## Practical Examples
### String-key dictionary (default keys, bstr values)
```c
#define FIO_MAP_NAME dict
#define FIO_MAP_VALUE_BSTR /* string values via fio_bstr */
#include "fio-stl.h"
/* Wrap set/get to salt the hash with the map pointer for security. */
static inline fio_str_info_s dict_put(dict_s *m, fio_str_info_s k, fio_str_info_s v) {
return dict_set(m, fio_risky_hash(k.buf, k.len, (uint64_t)(uintptr_t)m), k, v, NULL);
}
static inline fio_str_info_s dict_fetch(dict_s *m, fio_str_info_s k) {
return dict_get(m, fio_risky_hash(k.buf, k.len, (uint64_t)(uintptr_t)m), k);
}
void example(void) {
dict_s d = FIO_MAP_INIT;
dict_put(&d, FIO_STR_INFO1("hello"), FIO_STR_INFO1("world"));
fio_str_info_s v = dict_fetch(&d, FIO_STR_INFO1("hello"));
printf("%.*s\n", (int)v.len, v.buf); /* "world" */
dict_destroy(&d);
}
```
### Automatic hashing (FIO_MAP_HASH_FN)
```c
#define FIO_MAP_NAME imap
#define FIO_MAP_KEY int
#define FIO_MAP_VALUE float
#define FIO_MAP_HASH_FN(k) fio_risky_num((uint64_t)(k), 0)
#include "fio-stl.h"
void example(void) {
imap_s m = FIO_MAP_INIT;
imap_set(&m, 42, 3.14f, NULL);
printf("%f\n", imap_get(&m, 42)); /* 3.14 */
imap_destroy(&m);
}
```
### LRU cache (manual eviction)
```c
#define FIO_MAP_NAME cache
#define FIO_MAP_KEY_KSTR /* efficient internal string storage */
#define FIO_MAP_VALUE void *
#define FIO_MAP_LRU /* enables LRU ordering; value is ignored */
#define FIO_MAP_HASH_FN(k) fio_risky_hash((k).buf, (k).len, 0)
#include "fio-stl.h"
```
`FIO_MAP_LRU` only controls ordering. Enforce the size limit yourself after inserting:
```c
static inline void cache_put(cache_s *c, fio_str_info_s k, void *v) {
cache_set(c, k, v, NULL);
if (cache_count(c) > (1ULL << 12))
cache_evict(c, 1);
}
```
---
## Notes
- **`210 map2.h`** is a legacy implementation. It is not documented here and should not be used in new code.
- Pointer tagging (`FIO_PTR_TAG`) and reference counting (`FIO_REF_NAME`) are supported, same as all 200-range modules.
- The map internally stores a compact 8-bit hash fingerprint alongside each node to speed up probing. Full hash is cached per-node unless `FIO_MAP_RECALC_HASH 1` is set.
- Against hash-flooding: after `FIO_MAP_ATTACK_LIMIT` full collisions the map falls back to overwriting stale entries. Always salt hashes with a per-map value (e.g., the map pointer) when keys come from untrusted input.
---
See also: [← 200 types-overview.md](./200 types-overview.md) · [201 string.md](./201 string.md) · [202 array.md](./202 array.md)
# Reference Counting Wrapper — Module 249
Heap-allocate any type with an atomic reference count attached, generated from a name macro.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_REF_NAME my_obj /* required */
#define FIO_REF_TYPE my_obj_s /* optional — defaults to FIO_REF_NAME_s */
#define FIO_REF_CONSTRUCTOR_ONLY /* optional — see below */
#include "fio-stl.h"
```
This generates a thin wrapper struct that sits **before** the `FIO_REF_TYPE` object in memory. The caller only ever sees a `FIO_REF_TYPE *` (or the tagged pointer type if `FIO_PTR_TAG_TYPE` is set). The reference count, optional metadata, and optional flex-array length are all hidden in the prefix.
Throughout this document `REF` stands for whatever name you chose (e.g. `my_obj`).
> **Requires:** the `FIO_ATOMIC` helpers must be available (included automatically when using `fio-stl.h` as a single header).
---
## Configuration Macros
Set these **before** `#include`, after `FIO_REF_NAME`.
### Wrapped type
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_TYPE` | `FIO_REF_NAME_s` | The C type to wrap and reference-count. |
### Constructor / destructor naming
| Macro | Effect |
|---|---|
| _(not defined)_ | Generates `REF_new2`, `REF_dup2`, `REF_free2` — the `2` suffix leaves room for a separately-defined `REF_new` / `REF_free`. |
| `FIO_REF_CONSTRUCTOR_ONLY` | Generates `REF_new`, `REF_dup`, `REF_free` — use this when the ref-counted constructor **is** the primary constructor. |
### Object lifecycle hooks
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_INIT(obj)` | zero-fill if allocator doesn't guarantee it | Called on the newly allocated `FIO_REF_TYPE` object after allocation. `obj` is the dereferenced object (not a pointer). |
| `FIO_REF_DESTROY(obj)` | _(nothing)_ | Called on the `FIO_REF_TYPE` object just before memory is freed (when the last reference drops). |
### Metadata
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_METADATA` | _(not defined)_ | A type to embed as hidden metadata alongside the ref-counted object. Access it via `REF_metadata()`. |
| `FIO_REF_METADATA_INIT(meta)` | zero-fill if needed | Called on the metadata field after allocation. |
| `FIO_REF_METADATA_DESTROY(meta)` | _(nothing)_ | Called on the metadata field just before memory is freed. |
### Flexible array
| Macro | Default | Effect |
|---|---|---|
| `FIO_REF_FLEX_TYPE` | _(not defined)_ | When defined, the constructor allocates extra memory for a `FIO_REF_FLEX_TYPE[]` array immediately after the main struct. The constructor accepts a `size_t members` argument and stores the count internally. The `members` value is also available inside `FIO_REF_INIT`. |
> **Note:** `FIO_REF_FLEX_TYPE` shrinks the reference counter to 32 bits (instead of the native word size). Do not combine this with a custom `FIO_MEM_FREE` that depends on the byte-count argument, because the flex free call reports a size that omits `sizeof(FIO_REF_TYPE)`.
### Pointer tagging
If `FIO_PTR_TAG_TYPE` is defined (globally, before including), all generated functions accept and return `FIO_PTR_TAG_TYPE` instead of `FIO_REF_TYPE *`. Tags are applied on allocation (`FIO_PTR_TAG`) and stripped internally (`FIO_PTR_UNTAG`).
---
## Generated API
### Constructor
```c
/* standard */
FIO_REF_TYPE *REF_new(void); /* FIO_REF_CONSTRUCTOR_ONLY */
FIO_REF_TYPE *REF_new2(void); /* default */
/* with FIO_REF_FLEX_TYPE */
FIO_REF_TYPE *REF_new(size_t members);
FIO_REF_TYPE *REF_new2(size_t members);
```
Allocates and zero-initializes the wrapper header, calls `FIO_REF_METADATA_INIT` (if any), then calls `FIO_REF_INIT` on the wrapped object. Returns `NULL` on allocation failure. Initial reference count is **1**.
### Increment reference count
```c
FIO_REF_TYPE *REF_dup(const FIO_REF_TYPE *obj); /* FIO_REF_CONSTRUCTOR_ONLY */
FIO_REF_TYPE *REF_dup2(const FIO_REF_TYPE *obj); /* default */
```
Atomically increments the reference count. Returns the same pointer, or `NULL` if the input is `NULL`. Thread-safe.
### Decrement reference count / free
```c
void REF_free(FIO_REF_TYPE *obj); /* FIO_REF_CONSTRUCTOR_ONLY */
void REF_free2(FIO_REF_TYPE *obj); /* default */
```
Atomically decrements the reference count. When the count reaches zero, calls `FIO_REF_DESTROY(object)`, then `FIO_REF_METADATA_DESTROY(metadata)` (if any), then frees the backing memory. Thread-safe. No-op on `NULL`.
### Metadata access
```c
FIO_REF_METADATA *REF_metadata(FIO_REF_TYPE *obj);
```
Returns a pointer to the hidden metadata field. Only generated when `FIO_REF_METADATA` is defined.
### Flex array length
```c
uint32_t REF_metadata_flex_len(FIO_REF_TYPE *obj);
```
Returns the number of `FIO_REF_FLEX_TYPE` members allocated alongside the object. Only generated when `FIO_REF_FLEX_TYPE` is defined. Returns `0` for `NULL`.
### Debugging helper
```c
size_t REF_references(FIO_REF_TYPE *obj);
```
Returns the current reference count. **Do not use for program logic** — the value is inherently unstable in a concurrent context. Useful for assertions and leak-hunting.
---
## Examples
### Basic reference-counted object
```c
typedef struct { int x; int y; } point_s;
#define FIO_REF_NAME point
#define FIO_REF_TYPE point_s
#define FIO_REF_CONSTRUCTOR_ONLY /* point_new / point_dup / point_free */
#define FIO_REF_INIT(obj) (obj).x = 0; (obj).y = 0
#include "fio-stl.h"
void example(void) {
point_s *p = point_new(); /* ref count = 1 */
p->x = 10; p->y = 20;
point_s *alias = point_dup(p); /* ref count = 2 */
point_free(alias); /* ref count = 1 — not freed yet */
point_free(p); /* ref count = 0 — freed */
}
```
### Using the `2`-suffix when a primary constructor already exists
```c
/* Suppose FIO_STR_NAME already generated str_new / str_free. */
#define FIO_REF_NAME str
#define FIO_REF_TYPE str_s
/* No FIO_REF_CONSTRUCTOR_ONLY — generates str_new2 / str_dup2 / str_free2 */
#define FIO_REF_DESTROY(obj) str_destroy(&(obj))
#include "fio-stl.h"
str_s *s = str_new2(); /* ref-counted allocation */
str_write(s, "hello", 5); /* use the string API directly */
str_s *s2 = str_dup2(s);
str_free2(s2);
str_free2(s); /* calls str_destroy, then frees memory */
```
### Hidden metadata
```c
typedef struct { char name[64]; } widget_s;
typedef struct { uint64_t created_at; } widget_meta_s;
#define FIO_REF_NAME widget
#define FIO_REF_TYPE widget_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_METADATA widget_meta_s
#define FIO_REF_METADATA_INIT(m) (m).created_at = fio_time_real().tv_sec
#include "fio-stl.h"
void example(void) {
widget_s *w = widget_new();
widget_meta_s *m = widget_metadata(w);
printf("created at: %llu\n", (unsigned long long)m->created_at);
widget_free(w);
}
```
### Flexible array suffix
```c
typedef struct { size_t len; double data[]; } vec_s;
#define FIO_REF_NAME vec
#define FIO_REF_TYPE vec_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_FLEX_TYPE double
#define FIO_REF_INIT(obj) (obj).len = members /* 'members' is in scope */
#include "fio-stl.h"
void example(void) {
vec_s *v = vec_new(8); /* allocates vec_s + 8 doubles */
uint32_t n = vec_metadata_flex_len(v); /* returns 8 */
for (uint32_t i = 0; i < n; i++) v->data[i] = (double)i;
vec_free(v);
}
```
---
## Memory Layout
```
[ _wrapper_s header ] [ FIO_REF_TYPE object ] [ FIO_REF_FLEX_TYPE[] ] (optional)
^ ^
hidden prefix pointer returned to caller
```
The `_wrapper_s` header holds the reference count (and optional `flx_size` / `metadata` fields). Callers never see it — they only receive a pointer to the `FIO_REF_TYPE` that immediately follows.
The header's declared alignment matches `_Alignof(FIO_REF_TYPE)`, so its size rounds up to a multiple of that alignment and the object that follows it is always correctly aligned. Allocation goes through `FIO_MEM_REALLOC_ALIGNED_` with the type's alignment (the allocator treats smaller-than-default requests as the default), so over-aligned types work in every allocator configuration.
---
## Notes
- All reference-count operations use **atomic** instructions and are thread-safe.
- The module must be placed after any other type modules it wraps (e.g., after `FIO_STR_NAME` or `FIO_ARRAY_NAME` inclusions) when used together in one translation unit.
- Each `FIO_REF_NAME` definition is independent: you can wrap multiple types in the same file.
- `REF_references()` is a debugging aid; its return value is transient and must not drive control flow.
# FIOBJ — Soft Dynamic Types — Module 250
A unified soft-type system built on pointer tagging. One `FIOBJ` handle covers `null`, `true`, `false`, integers, floats, strings, arrays, and hash maps — plus user-defined extensions.
See also: [← 200 types-overview.md](./200 types-overview.md)
---
## Setup
```c
#define FIO_FIOBJ
#include "fio-stl.h"
```
No name macro is needed — `FIO_FIOBJ` activates the entire soft-type system in one step.
To expose symbols across multiple translation units add `FIO_EXTERN` before the include everywhere, and `FIO_EXTERN_COMPLETE` in exactly one `.c` file.
---
## How it Works
`FIOBJ` is a tagged pointer (`uintptr_t`). The bottom 3 bits encode the type class; the remaining bits hold the value or a heap pointer. Small integers and many floats require **no heap allocation** at all.
```
bits[2:0] type class
bits[63:3] value or aligned pointer
```
All heap-allocated types are reference-counted. Primitives, small numbers, and many floats are immediate values — `fiobj_dup` / `fiobj_free` are no-ops for them.
The system uses an **ownership** model:
- Placing a value into an Array or Hash Map **transfers ownership** to the container.
- Freeing the container frees its elements (Arrays own members; Hash Maps own keys and values).
- Call `fiobj_dup` before inserting if you need to keep an independent reference.
---
## Configuration Macros
Set these before `#include` to override defaults.
### Nesting / recursion
| Macro | Default | Effect |
|---|---|---|
| `FIOBJ_MAX_NESTING` | `512` | Maximum depth for `fiobj_each2` and JSON serialization/parsing. Does **not** apply to `fiobj_free`. |
| `FIOBJ_JSON_APPEND` | `1` | When `1`, JSON parsing appends to existing containers; when `0`, it replaces them. |
### Type-name prefixes
All generated type names can be renamed by defining the following macros before the include. The defaults produce the API names used throughout this document.
| Macro | Default | Resulting prefix |
|---|---|---|
| `FIOBJ___NAME_NULL` | `null` | `fiobj_null` |
| `FIOBJ___NAME_NUMBER` | `num` | `fiobj_num_*` |
| `FIOBJ___NAME_FLOAT` | `float` | `fiobj_float_*` |
| `FIOBJ___NAME_STRING` | `str` | `fiobj_str_*` |
| `FIOBJ___NAME_ARRAY` | `array` | `fiobj_array_*` |
| `FIOBJ___NAME_HASH` | `hash` | `fiobj_hash_*` |
---
## Type Identification
### Type constants
| Constant | Value | Description |
|---|---|---|
| `FIOBJ_T_INVALID` | `0` | Invalid / uninitialized FIOBJ |
| `FIOBJ_T_PRIMITIVE` | `2` | Raw pointer-tag class for all primitives |
| `FIOBJ_T_NULL` | `2` | `null` primitive |
| `FIOBJ_T_TRUE` | `18` | `true` primitive |
| `FIOBJ_T_FALSE` | `34` | `false` primitive |
| `FIOBJ_T_NUMBER` | `0x01` | Integer (small integers are unboxed) |
| `FIOBJ_T_FLOAT` | `0x06` | Float / double (many values are unboxed) |
| `FIOBJ_T_STRING` | `0x03` | Dynamic string |
| `FIOBJ_T_ARRAY` | `0x04` | Dynamic array |
| `FIOBJ_T_HASH` | `0x05` | Ordered hash map |
| `FIOBJ_T_OTHER` | `0x07` | Extension / user-defined type |
| `FIOBJ_INVALID` | `0` | The invalid FIOBJ sentinel value |
### Type-check macros
```c
size_t FIOBJ_TYPE(FIOBJ o); /* full type ID, including extensions */
int FIOBJ_TYPE_IS(FIOBJ o, type); /* 1 if type matches */
fiobj_class_en FIOBJ_TYPE_CLASS(FIOBJ o); /* raw 3-bit class (no extension lookup) */
int FIOBJ_IS_INVALID(FIOBJ o); /* true if o == FIOBJ_INVALID (0) */
int FIOBJ_IS_NULL(FIOBJ o); /* true if INVALID or null primitive */
```
`FIOBJ_TYPE` is the safe, recommended way to check type. `FIOBJ_TYPE_CLASS` is faster but may return `FIOBJ_T_OTHER` for heap-boxed numbers and floats. It also returns `FIOBJ_T_PRIMITIVE` (`2`) for `null`, `true`, and `false` alike — use `FIOBJ_TYPE` to distinguish primitives.
---
## Lifecycle — Reference Counting
```c
FIOBJ fiobj_dup(FIOBJ o); /* increment ref count; returns o */
void fiobj_free(FIOBJ o); /* decrement ref count; frees when zero */
```
Primitives, unboxed integers, and unboxed floats are immune — `fiobj_dup` / `fiobj_free` are no-ops for them. Always call `fiobj_free` on every FIOBJ you own.
**Warning:** `fiobj_free` is recursive. Deeply nested structures can overflow the stack. `FIOBJ_MAX_NESTING` does **not** protect it. Use the JSON parser (which does protect against nesting attacks) when handling untrusted input.
```c
/* build a small object graph */
FIOBJ arr = fiobj_array_new();
fiobj_array_push(arr, fiobj_num_new(42)); /* array owns the number */
fiobj_array_push(arr, fiobj_str_new_cstr("hi", 2)); /* array owns the string */
FIOBJ ref = fiobj_dup(arr); /* arr and ref both point to same array */
fiobj_free(ref); /* decrement; array still alive */
fiobj_free(arr); /* decrement to zero → frees array + contents */
```
---
## Generic Accessors
These work on **any** FIOBJ type.
```c
fio_str_info_s fiobj2cstr(FIOBJ o); /* string representation (temporary) */
intptr_t fiobj2i(FIOBJ o); /* integer representation */
double fiobj2f(FIOBJ o); /* float representation */
uint64_t fiobj2hash(FIOBJ o); /* hash value (for use as map key) */
unsigned char fiobj_is_eq(FIOBJ a, FIOBJ b); /* deep equality */
```
Conversion behaviour by type:
| Object type | `fiobj2i` | `fiobj2f` | `fiobj2cstr` |
|---|---|---|---|
| `null` / invalid | `0` | `0.0` | `"null"` / `""` |
| `true` | `1` | `1.0` | `"true"` |
| `false` | `0` | `0.0` | `"false"` |
| Number | the integer | cast to double | base-10 string |
| Float | floor to integer | the double | decimal string |
| String | `fio_atol` of content | `fio_atof` of content | raw content |
| Array | element count | element count as double | `"[...]"` |
| Hash | key-value pair count | count as double | `"{...}"` |
`fiobj_is_eq` performs deep structural comparison for Arrays and Hash Maps.
`fiobj2cstr` returns a temporary view. For numbers/floats the buffer is thread-local (safe for up to 128 concurrent threads). For heap strings it points directly into the string's buffer.
---
## Primitives — null, true, false
The three primitives are immediate constants. They never allocate memory and never need `fiobj_free`.
```c
FIOBJ fiobj_null(void); /* returns the null primitive */
FIOBJ fiobj_true(void); /* returns the true primitive */
FIOBJ fiobj_false(void); /* returns the false primitive */
```
```c
FIOBJ v = fiobj_null();
if (FIOBJ_IS_NULL(v)) /* also true for FIOBJ_INVALID (0) */
printf("got null\n");
if (FIOBJ_TYPE_IS(v, FIOBJ_T_NULL)) /* strictly the null primitive */
printf("strict null\n");
```
---
## Numbers — `fiobj_num_*`
Small integers are stored in the tagged pointer itself — no allocation. Large values that don't fit are heap-boxed transparently.
```c
FIOBJ fiobj_num_new(intptr_t i); /* create */
intptr_t fiobj_num2i(FIOBJ i); /* read as integer */
double fiobj_num2f(FIOBJ i); /* read as double */
fio_str_info_s fiobj_num2cstr(FIOBJ i); /* base-10 string (temp) */
void fiobj_num_free(FIOBJ i); /* type-specific free */
```
`fiobj_num_free` is a fast alternative to `fiobj_free` when the type has already been validated.
---
## Floats — `fiobj_float_*`
Many double values fit in the tagged pointer (those whose lower 3 bits of the IEEE 754 representation are zero). Others are heap-boxed.
```c
FIOBJ fiobj_float_new(double i); /* create */
intptr_t fiobj_float2i(FIOBJ i); /* floor to integer */
double fiobj_float2f(FIOBJ i); /* read as double */
fio_str_info_s fiobj_float2cstr(FIOBJ i); /* decimal string (temp) */
void fiobj_float_free(FIOBJ i); /* type-specific free */
```
---
## Strings — `fiobj_str_*`
FIOBJ strings wrap the [Dynamic String module (201)](./201 string.md). The full `fiobj_str_*` API mirrors the `STR_*` API — write, concat, printf, base64, JSON-escape, UTF-8 helpers, freeze, etc.
### Constructors
```c
FIOBJ fiobj_str_new(void); /* empty string */
FIOBJ fiobj_str_new_cstr(const char *ptr, size_t len); /* copy from C string */
FIOBJ fiobj_str_new_buf(size_t capa); /* empty with reserved capacity */
FIOBJ fiobj_str_new_copy(FIOBJ original); /* copy from any FIOBJ (via fiobj2cstr) */
```
### Common operations
```c
fio_str_info_s fiobj_str2cstr(FIOBJ s); /* same as fiobj_str_info() */
void fiobj_str_write(FIOBJ s, const char *buf, size_t len);
void fiobj_str_printf(FIOBJ s, const char *fmt, ...);
void fiobj_str_concat(FIOBJ dest, FIOBJ src); /* append src string */
void fiobj_str_freeze(FIOBJ s); /* mark immutable */
void fiobj_str_free(FIOBJ s); /* type-specific free */
```
### Stack-allocated temporary strings
```c
/* Empty temporary string on the stack */
FIOBJ_STR_TEMP_VAR(name);
/* Temporary string wrapping a static buffer (read-only view) */
FIOBJ_STR_TEMP_VAR_STATIC(name, buf, len);
/* Temporary string wrapping an existing read/write buffer */
FIOBJ_STR_TEMP_VAR_EXISTING(name, buf, len, capa);
/* Always call when done — releases any dynamic allocation */
FIOBJ_STR_TEMP_DESTROY(name);
```
Temporary strings live on the stack and are valid FIOBJ values. They must **not** be passed to containers or freed with `fiobj_free`.
```c
/* example: use a stack string as a hash key without heap allocation */
FIOBJ_STR_TEMP_VAR_STATIC(key, "name", 4);
FIOBJ val = fiobj_hash_get(hash, key);
FIOBJ_STR_TEMP_DESTROY(key);
```
---
## Arrays — `fiobj_array_*`
FIOBJ arrays wrap the [Dynamic Array module (202)](./202 array.md). The element type is `FIOBJ`; elements are owned by the array.
### Common operations
```c
FIOBJ fiobj_array_new(void);
void fiobj_array_free(FIOBJ a);
FIOBJ *fiobj_array_push(FIOBJ a, FIOBJ value); /* append; transfers ownership; NULL on error */
int fiobj_array_pop(FIOBJ a, FIOBJ *old); /* remove last; stores in *old or frees if NULL */
FIOBJ fiobj_array_get(FIOBJ a, int64_t index); /* access; NO ownership transfer */
FIOBJ *fiobj_array_set(FIOBJ a, int64_t index, FIOBJ value, FIOBJ *old); /* old in *old or freed */
int fiobj_array_remove(FIOBJ a, int64_t index, FIOBJ *old); /* remove; optional ownership transfer */
uint32_t fiobj_array_count(FIOBJ a);
void fiobj_array_concat(FIOBJ dest, FIOBJ src); /* appends copies of src elements */
```
Negative indices count from the end (`-1` = last element).
```c
FIOBJ a = fiobj_array_new();
fiobj_array_push(a, fiobj_num_new(1));
fiobj_array_push(a, fiobj_num_new(2));
fiobj_array_push(a, fiobj_str_new_cstr("three", 5));
printf("count: %u\n", fiobj_array_count(a)); /* 3 */
printf("first: %ld\n", fiobj2i(fiobj_array_get(a, 0))); /* 1 */
fiobj_free(a); /* frees array and all three elements */
```
---
## Hash Maps — `fiobj_hash_*`
FIOBJ hash maps are **ordered** maps keyed and valued by FIOBJ. They wrap the [Hash Map module (210)](./210 map.md). The map owns stored keys and values. The low-level `fiobj_hash_set` duplicates FIOBJ keys, so the caller still owns its original key reference; C-string helpers copy the key string internally.
### Core operations
```c
FIOBJ fiobj_hash_new(void);
void fiobj_hash_free(FIOBJ h);
uint32_t fiobj_hash_count(FIOBJ h);
/* FIOBJ-key variants (low level) */
FIOBJ fiobj_hash_set(FIOBJ h, FIOBJ key, FIOBJ value, FIOBJ *old);
FIOBJ fiobj_hash_get(FIOBJ h, FIOBJ key);
int fiobj_hash_remove(FIOBJ h, FIOBJ key, FIOBJ *old);
```
### C-string key helpers (recommended)
```c
/* set: copies key string, transfers value ownership */
FIOBJ fiobj_hash_set2(FIOBJ h, const char *key, size_t len, FIOBJ value);
/* get: no allocation, no ownership transfer */
FIOBJ fiobj_hash_get2(FIOBJ h, const char *key, size_t len);
/* remove: returns 0 on success; puts old value in *old (or frees it if NULL) */
int fiobj_hash_remove2(FIOBJ h, const char *key, size_t len, FIOBJ *old);
```
### Merge
```c
/* deep merge: arrays concatenate, nested hashes recurse, scalars overwrite;
null/invalid values in src remove the matching key from dest. */
void fiobj_hash_update(FIOBJ dest, FIOBJ src);
```
```c
FIOBJ h = fiobj_hash_new();
fiobj_hash_set2(h, "name", 4, fiobj_str_new_cstr("Alice", 5));
fiobj_hash_set2(h, "age", 3, fiobj_num_new(30));
FIOBJ name = fiobj_hash_get2(h, "name", 4);
printf("%s\n", fiobj2cstr(name).buf); /* Alice */
fiobj_free(h); /* frees map, values, and key strings */
```
---
## Iteration
### Shallow — `fiobj_each1`
Iterates direct children of an Array or Hash Map.
```c
typedef struct fiobj_each_s {
FIOBJ const parent; /* the container being iterated */
uint64_t index; /* current element index */
int (*task)(struct fiobj_each_s *info); /* callback (may be swapped mid-loop) */
void *udata; /* caller-supplied data */
FIOBJ value; /* current element value */
FIOBJ key; /* current key (Hash Maps only) */
} fiobj_each_s;
uint32_t fiobj_each1(FIOBJ o,
int (*task)(fiobj_each_s *info),
void *udata,
int32_t start_at);
```
Return `-1` from the callback to stop early. Returns the stop position (`elements processed + start_at`).
```c
int print_item(fiobj_each_s *e) {
printf("[%llu] %s\n", (unsigned long long)e->index,
fiobj2cstr(e->value).buf);
return 0;
}
fiobj_each1(arr, print_item, NULL, 0);
```
### Deep — `fiobj_each2`
Recursively walks the object and all nested containers, as if flattened.
```c
uint32_t fiobj_each2(FIOBJ o,
int (*task)(fiobj_each_s *info),
void *udata);
```
Respects `FIOBJ_MAX_NESTING`. The root object itself is also passed to the callback (`index == 0`, `parent == FIOBJ_INVALID`).
---
## JSON
### Serialize to JSON
```c
FIOBJ fiobj2json(FIOBJ dest, FIOBJ o, uint8_t beautify);
```
Returns a `FIOBJ` string containing the JSON representation of `o`. If `dest` is an existing FIOBJ string, the JSON is **appended** to it; otherwise a new string is created. Pass `beautify != 0` for indented output.
```c
FIOBJ obj = fiobj_json_parse2("{\"x\":1,\"y\":[2,3]}", 18, NULL);
FIOBJ json = fiobj2json(FIOBJ_INVALID, obj, 1); /* beautified */
printf("%s\n", fiobj2cstr(json).buf);
fiobj_free(json);
fiobj_free(obj);
```
### Parse JSON
```c
/* parse a full JSON value */
FIOBJ fiobj_json_parse(fio_str_info_s str, size_t *consumed);
/* convenience macro */
#define fiobj_json_parse2(data, len, consumed) \
fiobj_json_parse(FIO_STR_INFO2(data, len), consumed)
```
Returns `FIOBJ_INVALID` on parse error. If `consumed` is non-NULL it is set to the number of bytes read.
### Merge JSON into a Hash Map
```c
size_t fiobj_hash_update_json(FIOBJ hash, fio_str_info_s str);
size_t fiobj_hash_update_json2(FIOBJ hash, char *ptr, size_t len);
```
Parses JSON and merges the resulting key-value pairs into an existing hash map. Silently skips non-object JSON data. Returns bytes consumed (0 on error).
### JavaScript-style path lookup
```c
FIOBJ fiobj_json_find(FIOBJ object, fio_str_info_s notation);
#define fiobj_json_find2(object, str, length) \
fiobj_json_find(object, FIO_STR_INFO2(str, length))
```
Finds a nested value using dot/bracket notation. For example `"[0].name"` returns the `name` field of the first array element. Returns a **temporary reference** — call `fiobj_dup` if you need to keep it.
```c
FIOBJ data = fiobj_json_parse2("[{\"name\":\"Bob\"}]", 16, NULL);
FIOBJ name = fiobj_json_find2(data, "[0].name", 8);
printf("%s\n", fiobj2cstr(name).buf); /* Bob */
fiobj_free(data);
/* name was a temporary reference into data; do not free separately */
```
---
## Mustache Templates
Renders a compiled Mustache template using FIOBJ data (typically a Hash Map) as context.
```c
/* create a new FIOBJ string with the rendered output */
FIOBJ fiobj_mustache_build(fio_mustache_s *m, FIOBJ ctx);
/* append rendered output to an existing FIOBJ string */
FIOBJ fiobj_mustache_build2(fio_mustache_s *m, FIOBJ dest, FIOBJ ctx);
```
Both may return `FIOBJ_INVALID` if nothing was written and `dest` was empty/invalid. See the Mustache module for how to compile a template (`fio_mustache_s *`).
---
## Extending FIOBJ
Custom types integrate via a virtual function table tagged as `FIOBJ_T_OTHER`.
### Requirements
1. Choose a **unique** type ID ≥ 100. Values below 100 are reserved; values below 40 are illegal.
2. Populate a `FIOBJ_class_vtable_s` — all function pointers must be non-NULL.
3. Wrap the type with `FIO_REF_NAME` (see [249 reference counter.md](./249 reference counter.md)), setting `FIO_REF_METADATA` to `const FIOBJ_class_vtable_s *` and initialising it in `FIO_REF_METADATA_INIT`.
4. Apply pointer tagging so all pointers carry `FIOBJ_T_OTHER` in the low 3 bits.
```c
#define FIO_PTR_TAG(p) FIOBJ_PTR_TAG(p, FIOBJ_T_OTHER)
#define FIO_PTR_UNTAG(p) FIOBJ_PTR_UNTAG(p)
#define FIO_PTR_TAG_TYPE FIOBJ
```
### Virtual function table
```c
typedef struct {
size_t type_id; /* unique ID ≥ 100 */
unsigned char (*is_eq)(FIOBJ a, FIOBJ b);
fio_str_info_s (*to_s)(FIOBJ o);
intptr_t (*to_i)(FIOBJ o);
double (*to_f)(FIOBJ o);
uint32_t (*count)(FIOBJ o); /* number of child elements */
uint32_t (*each1)(FIOBJ o, int (*task)(fiobj_each_s *), void *udata, int32_t start_at);
void (*free2)(FIOBJ o); /* free when ref count reaches zero */
} FIOBJ_class_vtable_s;
```
All slots must be filled. If the type has no children, `count` should return `0` and `each1` should be a no-op that returns `0`.
### Minimal example
```c
#define FIOBJ_T_POINT 100UL
typedef struct { double x, y; } point_data_s;
static unsigned char point_eq(FIOBJ a, FIOBJ b) { /* ... */ return 0; }
static fio_str_info_s point_to_s(FIOBJ o) { /* ... */ return (fio_str_info_s){0}; }
static intptr_t point_to_i(FIOBJ o) { return 0; }
static double point_to_f(FIOBJ o) { return 0.0; }
static uint32_t point_count(FIOBJ o) { return 0; (void)o; }
static uint32_t point_each1(FIOBJ o, int (*t)(fiobj_each_s *), void *u, int32_t s)
{ return 0; (void)o;(void)t;(void)u;(void)s; }
static void point_free2(FIOBJ o); /* forward declaration */
static const FIOBJ_class_vtable_s FIOBJ___POINT_VTBL = {
.type_id = FIOBJ_T_POINT,
.is_eq = point_eq, .to_s = point_to_s,
.to_i = point_to_i, .to_f = point_to_f,
.count = point_count, .each1 = point_each1,
.free2 = point_free2,
};
#define FIO_REF_NAME fiobj_point
#define FIO_REF_TYPE point_data_s
#define FIO_REF_CONSTRUCTOR_ONLY
#define FIO_REF_METADATA const FIOBJ_class_vtable_s *
#define FIO_REF_METADATA_INIT(m) (m = &FIOBJ___POINT_VTBL)
#define FIO_PTR_TAG(p) FIOBJ_PTR_TAG(p, FIOBJ_T_OTHER)
#define FIO_PTR_UNTAG(p) FIOBJ_PTR_UNTAG(p)
#define FIO_PTR_TAG_TYPE FIOBJ
#include "fio-stl.h"
static void point_free2(FIOBJ o) { fiobj_point_free(o); }
FIOBJ fiobj_point_new_xy(double x, double y) {
FIOBJ o = fiobj_point_new();
point_data_s *p = (point_data_s *)FIOBJ_PTR_UNTAG(o);
p->x = x; p->y = y;
return o;
}
```
---
## Debugging / Leak Detection
When compiled with `TEST`, `DEBUG`, or `FIO_LEAK_COUNTER` defined, two global counters track allocations:
```c
size_t FIOBJ_MARK_MEMORY_ALLOC_COUNTER;
size_t FIOBJ_MARK_MEMORY_FREE_COUNTER;
```
Call `FIOBJ_MARK_MEMORY_PRINT()` after all objects should be freed to log any leaks.
# IO and HTTP — Group Overview (400-range)
```c
#define FIO_IO /* IO Reactor */
#define FIO_IPC /* Inter-Process Communication */
#define FIO_PUBSUB /* Pub/Sub */
#define FIO_REDIS /* Redis engine */
#define FIO_HTTP /* HTTP server / client */
#include "fio-stl.h"
```
The 400-range is where facil.io becomes a networked server. A single-threaded
evented IO reactor sits at the foundation, optional TLS plugs into it as a
transport hook, IPC ferries work between worker processes, PubSub builds
broadcast semantics on top of IPC, Redis extends PubSub across machines, and
HTTP closes the loop with a production-grade server that speaks HTTP/1.x,
WebSocket, and SSE.
---
## Stack Diagram
```
┌──────────────────────────────────────────────────────────────────┐
│ HTTP (439 http.h) │
│ fio_http_listen · fio_http_connect │
│ HTTP/1.x · WebSocket upgrades · SSE (EventSource) │
└──────┬─────────────────────────────────────────┬─────────────────┘
│ uses (internal) │ optional
▼ ▼
┌──────────────────────────────┐ ┌─────────────────────────────┐
│ HTTP parsers (004) │ │ Pub/Sub (420) │
│ · HTTP/1.1 parser │ │ subscribe · publish │
│ · WebSocket parser (RFC6455) │ │ pattern matching · replay │
│ · HTTP Handle (internal) │ │ pluggable engine interface │
└──────────────────────────────┘ └──────────────┬──────────────┘
│ engine
┌──────────────▼──────────────┐
│ Redis (422 redis.h) │
│ connects via IO (master) │
│ workers route through IPC │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ IPC (404 ipc.h) │
│ worker ↔ master messages │
│ cluster RPC (multi-machine) │
│ encrypted: ChaCha20-Poly1305│
└──────────────┬──────────────┘
│ built on
┌───────────────────────────────────────────────────▼──────────────┐
│ IO Reactor (401–403) │
│ fio_io_s · fio_io_protocol_s epoll / kqueue / poll │
│ fio_io_listen · fio_io_connect fio_io_defer · fio_io_run_every │
│ fio_io_async_s (background thread pools for non-IO tasks) │
└──────────────────────────────────┬───────────────────────────────┘
│ transport hooks (fio_io_functions_s)
▼
┌─────────────────────────┐
│ TLS (405) │
│ OpenSSL 3.x (preferred) │
│ or native TLS 1.3 │
│ auto-selected at build │
└─────────────────────────┘
```
---
## Layer-by-Layer
### IO Reactor — 401 · 402 · 403
**Enable with:** `#define FIO_IO`
**Docs:** [./401 io api.md](./401 io api.md)
The event loop. `fio_io_start(workers)` blocks and runs forever (or until
`fio_io_stop()`). With `workers > 0` it forks child processes; the master stays
alive to manage IPC and listeners while workers handle connections.
Key types and functions:
- `fio_io_s` — opaque IO handle (connection). Never store raw; use
`fio_io_dup` / `fio_io_free` for lifetime management.
- `fio_io_protocol_s` — struct of callbacks (`on_attach`, `on_data`,
`on_ready`, `on_shutdown`, `on_timeout`, `on_close`) shared by all
connections of the same protocol family.
- `fio_io_listen(...)` — attach a listening socket with a protocol.
- `fio_io_connect(...)` — connect as a client.
- `fio_io_attach_fd(fd, protocol, udata, tls)` — adopt a raw fd.
- `fio_io_read / fio_io_write / fio_io_close` — IO operations (safe only
inside callbacks or deferred tasks).
- `fio_io_defer(task, u1, u2)` — schedule a task on the IO thread (thread-safe).
- `fio_io_run_every(...)` — schedule a recurring timer.
`fio_io_async_s` / `fio_io_async_attach(q, threads)` spins up a background
thread pool for CPU-heavy or blocking work that must not block the IO loop.
---
### TLS — 405 openssl.h · 405 tls13.h
**Enable with:** `#define FIO_IO` (TLS backends auto-include when available)
**Docs:** [./405 openssl.md](./405 openssl.md) · [./405 tls13.md](./405 tls13.md) *(planned)*
TLS is plugged into the reactor through `fio_io_functions_s` — a set of
transport callbacks (`build_context`, `free_context`, `start`, `read`, `write`,
`flush`, `finish`, `cleanup`) that replace the default socket calls.
The `fio_io_tls_s` context carries certificates, ALPN negotiation entries, and
trusted CA chains. It is built from `fio_io_tls_new()` / `fio_io_tls_cert_add()`
/ `fio_io_tls_alpn_add()` / `fio_io_tls_trust_add()`, or parsed directly from a
URL query string:
```c
fio_io_listen(.url = "0.0.0.0:443/?tls=./certs/", .protocol = &my_proto);
```
Two backends are available — OpenSSL 3.x (`fio_openssl_io_functions()`) and
the native TLS 1.3 engine (`fio_tls13_io_functions()`). The default is
selected automatically at build time; override with
`fio_io_tls_default_functions(&my_io_functions)` before `fio_io_start`.
> **Disambiguation:** `405` documents IO-layer TLS integration — how TLS wraps
> network connections in the reactor. The standalone TLS 1.3 crypto library
> (key schedule, record layer, handshake state machines, certificate helpers)
> lives separately at [./190 tls13.md](./190 tls13.md) and is what `405` builds on.
---
### IPC — 404 ipc.h
**Enable with:** `#define FIO_IPC`
**Docs:** [./404 ipc.md](./404 ipc.md) *(planned)*
Workers need to talk to the master. IPC handles this over a Unix socket (or
optionally TCP for multi-machine clusters). Messages are reference-counted,
encrypted in transit (ChaCha20-Poly1305, keyed from the process secret), and
routed by flags:
| Macro | Delivery |
|-------|----------|
| `fio_ipc_call(...)` | master only |
| `fio_ipc_local(...)` | master + all local workers |
| `fio_ipc_cluster(...)` | master on every machine |
| `fio_ipc_broadcast(...)` | master + workers on every machine |
For cross-machine RPC, register a non-zero op-code with
`fio_ipc_opcode_register(...)` before `fio_io_start`. Function-pointer messages
("fast path") work only within a single machine.
---
### Pub/Sub — 420 pubsub.h
**Enable with:** `#define FIO_PUBSUB` (requires `FIO_IPC`)
**Docs:** [./420 pubsub.md](./420 pubsub.md) *(planned)*
Pub/Sub delivers messages to named channels across all workers and, via
registered engines, across machines. The core API:
```c
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = my_callback,
.io = client_io);
fio_pubsub_publish(.channel = FIO_BUF_INFO1("news"),
.message = FIO_BUF_INFO1("breaking!"));
```
Subscriptions can be owned by an `fio_io_s` (auto-cancelled on close) or by
the global environment. Pattern subscriptions and numeric namespace `filter`
values are supported. An `engine` field selects an external backend (Redis,
custom). Replay from a timestamp is supported when history engines are attached.
---
### Redis — 422 redis.h
**Enable with:** `#define FIO_REDIS` (requires `FIO_PUBSUB`)
**Docs:** [./422 redis.md](./422 redis.md) *(planned)*
Redis integration as a PubSub engine *and* as a standalone command client.
Only the master process connects to the Redis server — workers route all
commands and publishes through IPC. This avoids multiplying Redis connections
and preserves correct subscribe/unsubscribe semantics.
```c
/* attach as a pub/sub engine */
fio_pubsub_engine_s *r = fio_redis_new(.url = "redis://localhost:6379");
/* send arbitrary Redis commands (not SUBSCRIBE/UNSUBSCRIBE) */
fio_redis_send(r, my_fiobj_command_array, my_reply_cb, NULL);
```
The return type is `fio_pubsub_engine_s *` — attach it to a pub/sub
subscription via the `engine` field. Reference counting
(`fio_redis_dup` / `fio_redis_free`) governs the engine lifetime.
All mutation runs on the IO thread via `fio_io_defer`.
---
### HTTP Parsers — 004 http1 parser.h · 004 websocket parser.h
**Enable with:** `#define FIO_HTTP1_PARSER` / `#define FIO_WEBSOCKET_PARSER`
**Docs:** [./004 http1 parser.md](./004 http1 parser.md) · [./004 websocket parser.md](./004 websocket parser.md)
Zero-allocation, event-driven parsers — no internal buffering, no heap.
- **HTTP/1.1 parser** (`fio_http1_parse`): parses request/response lines,
headers, and bodies (including chunked). Fires user-implemented callbacks
(`fio_http1_on_method`, `fio_http1_on_url`, `fio_http1_on_header`,
`fio_http1_on_body_chunk`, `fio_http1_on_complete`, …).
- **WebSocket parser** (`fio_websocket_parse`): RFC 6455 frame parser.
Zero-allocation, cache-sized. Produces typed events
(`FIO_WEBSOCKET_EV_DATA_CHUNK`, `FIO_WEBSOCKET_EV_CONTROL`,
`FIO_WEBSOCKET_EV_MESSAGE_END`, `FIO_WEBSOCKET_EV_ERROR`).
These are the building blocks used internally by the HTTP layer. Direct use
is for protocol-level work or embedding the parsers in a custom IO protocol.
**HTTP Handle** (internal — core in `432 http types.h`) is the
`fio_http_s` request/response state object with header
cache, body (RAM or file), and logging support. The handle is fully covered by the
HTTP documentation; there is no separate public handle doc.
---
### HTTP — 439 http.h
**Enable with:** `#define FIO_HTTP`
**Docs:** [./439 http.md](./439 http.md)
The top-level server and client. `fio_http_listen` registers an HTTP service
on the IO reactor; `fio_http_connect` opens an HTTP client connection.
```c
fio_http_listen("0.0.0.0:3000", .on_http = handle_request);
fio_io_start(0); /* or fio_io_start(4) for 4 worker processes */
```
The `fio_http_settings_s` struct wires up:
- `on_http` — HTTP request/response callback
- `on_open` / `on_message` / `on_close` — WebSocket lifecycle
- `on_eventsource` — SSE event callback
- `on_authenticate_websocket` / `on_authenticate_sse` — upgrade guards
- `tls` / `tls_io_func` — optional TLS context
- `queue` — optional `fio_io_async_s` for off-thread response work
- `public_folder` — static file serving with `gz` pre-compressed support
HTTP handles (`fio_http_s`) are created per request and carry headers, body,
and state. The `fio_http_s` lifetime is managed by the library; callbacks
receive a pointer valid only for the duration of the call (or while an
explicit reference is held).
---
## Threading and Reactor Model
The IO reactor is **single-threaded per process**. All protocol callbacks
(`on_data`, `on_ready`, etc.) run in the reactor thread of the process that
owns the connection. There is no locking required for IO operations.
**Process model** (when `fio_io_start(workers)` is called with `workers > 0`):
```
master process
├── owns IPC listener socket
├── runs PRE_START state callbacks
├── forwards cluster / pub/sub to workers
└── worker process × N
├── each gets its own IO reactor loop
├── accepts connections (listeners are dup'd per worker)
└── communicates back via IPC socket
```
Workers are forked; the master sentinel thread monitors each child and
respawns crashed workers automatically (configurable via
`FIO_ASSERT_DEBUG` in debug builds to prevent respawn masking crashes).
**Deferred work** (`fio_io_defer`) posts tasks onto the reactor queue —
safe to call from any thread. The reactor processes the queue after each
poll cycle.
**Timers** (`fio_io_run_every`) schedule repeating or one-shot callbacks
aligned to the reactor tick (millisecond resolution). Timer precision depends
on the poll timeout and reactor load.
**Background threads** (`fio_io_async_s`): for blocking or CPU-intensive
work that must not stall the IO loop, attach a thread pool with
`fio_io_async_attach(q, thread_count)`. The pool starts and stops with the
reactor automatically.
**Connection lifecycle:**
```
fd created (accept / connect)
│
└─► fio_io_attach_fd → on_attach
│
├─ on_data (data available to read)
├─ on_ready (outgoing buffer drained)
├─ on_timeout (idle too long; default: close)
│
├─ on_shutdown (reactor shutting down)
└─ on_close (connection closed — cleanup here)
```
Outgoing writes are buffered. When the buffer exceeds
`FIO_IO_THROTTLE_LIMIT` (default 2 MiB), `on_data` events are suspended
until the buffer drains ("throttling"). The shutdown sequence gives
connections up to `FIO_IO_SHUTDOWN_TIMEOUT` (default 15 s) to flush before
force-closing.
**`fio_io_env_set` / `fio_io_env_get`**: attach named objects to the
connection's lifetime. The `on_close` callback fires automatically when the
connection closes, enabling RAII-style per-connection resource management.
---
## TLS Disambiguation
There are **two separate TLS documents** in this library:
| Document | What it covers |
|----------|----------------|
| [`./405 tls13.md`](./405 tls13.md) *(planned)* | IO-layer TLS integration — how TLS is plugged into the IO reactor via `fio_io_functions_s`. Use this when setting up TLS listeners or clients. |
| [`./190 tls13.md`](./190 tls13.md) | Standalone TLS 1.3 crypto library — key schedule, record layer, handshake state machines, alerts, KeyUpdate, certificate helpers. The raw machinery. |
If you are adding TLS to a server or client, start with the `405` docs.
If you are implementing a custom TLS backend or need direct access to the TLS 1.3 internals, start with `./190 tls13.md`.
---
## Documentation Index
| Header | Module | Doc |
|--------|--------|-----|
| `401 io api.h` | IO Reactor API | [./401 io api.md](./401 io api.md) |
| `402 io types.h` | IO Types (internal) | covered by IO docs |
| `403 io reactor.h` | IO Reactor cycle (internal) | covered by IO docs |
| `404 ipc.h` | IPC | [./404 ipc.md](./404 ipc.md) |
| `405 openssl.h` | OpenSSL TLS backend | [./405 openssl.md](./405 openssl.md) |
| `405 tls13.h` | Native TLS 1.3 IO backend | [./405 tls13.md](./405 tls13.md) |
| `420 pubsub.h` | Pub/Sub | [./420 pubsub.md](./420 pubsub.md) |
| `422 redis.h` | Redis engine | [./422 redis.md](./422 redis.md) |
| `004 http1 parser.h` | HTTP/1.1 parser | [./004 http1 parser.md](./004 http1 parser.md) |
| `004 websocket parser.h` | WebSocket parser | [./004 websocket parser.md](./004 websocket parser.md) |
| `432 http types.h` | HTTP types / handle (internal) | covered by HTTP docs |
| `439 http.h` | HTTP server / client | [./439 http.md](./439 http.md) |
# IO Reactor API (401 io api.h)
```c
#define FIO_IO
#include FIO_INCLUDE_FILE
```
`FIO_IO` adds the facil.io evented IO reactor, connection handles, protocol
callbacks, listener/client helpers, TLS transport hooks, per-connection
lifetime storage, timers, and optional async worker queues.
See [./400 io-overview.md](./400 io-overview.md) for the full IO / IPC /
PubSub / HTTP stack. TLS backends are covered by
[./405 openssl.md](./405 openssl.md) and [./405 tls13.md](./405 tls13.md).
Socket helpers are documented in [./004 sock.md](./004 sock.md), timers and
queues in [./102 queue.md](./102 queue.md), and lifecycle callbacks in
[./004 state callbacks.md](./004 state callbacks.md).
---
## Configuration Macros
Define before including the header to override defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_IO_BUFFER_PER_WRITE` | `65536U` | Stack buffer size used during write events. |
| `FIO_IO_THROTTLE_LIMIT` | `2097152U` | `on_data` is throttled while outgoing backlog is large. |
| `FIO_IO_TIMEOUT_MAX` | `300000` | Maximum and default connection timeout, in milliseconds. |
| `FIO_IO_SHUTDOWN_TIMEOUT` | `15000` | Hard timeout for the reactor shutdown loop, in milliseconds. |
| `FIO_IO_COUNT_STORAGE` | `1` in `DEBUG`, else `0` | Enables IO byte-count storage when compiled in. |
---
## Core Types
| Type | Role |
|------|------|
| `fio_io_s` | Opaque connection handle. Use it instead of raw file descriptors inside protocol callbacks. |
| `fio_io_protocol_s` | Shared protocol callback table for a family of connections. Usually static / global and zero-initialized. |
| `fio_io_functions_s` | Optional transport vtable used to override socket IO, mainly for TLS. |
| `fio_io_tls_s` | Reference-counted TLS settings object consumed by transport backends. |
| `fio_io_listener_s` | Listener handle returned by `fio_io_listen`. |
| `fio_pubsub_msg_s` | Message delivered to `on_pubsub` callbacks; defined by the Pub/Sub module. |
| `fio_io_async_s` | Async worker queue attached to the reactor lifecycle. |
---
## Reactor Lifecycle and State
### Start / stop
```c
void fio_io_start(int workers);
void fio_io_stop(void);
void fio_io_add_workers(int workers);
void fio_io_restart(int workers);
void fio_io_restart_on_signal(int signal);
size_t fio_io_shutdown_timeout(void);
size_t fio_io_shutdown_timeout_set(size_t milliseconds);
```
`fio_io_start(workers)` starts the reactor and blocks until shutdown. A positive
`workers` value forks worker processes. `fio_io_stop()` asks the reactor to
stop. `fio_io_add_workers` and `fio_io_restart` are cluster/process helpers;
`fio_io_restart_on_signal` binds hot restart to a signal.
`fio_io_shutdown_timeout_set` changes the hard shutdown grace period and returns
the value that will be used.
### State queries
```c
int fio_io_is_running(void);
int fio_io_is_master(void);
int fio_io_is_worker(void);
uint16_t fio_io_workers(int workers_requested);
int fio_io_pid(void);
int fio_io_root_pid(void);
int64_t fio_io_last_tick(void);
int64_t fio_io_last_tick_time(void);
```
`fio_io_last_tick()` is the cached millisecond value from the last reactor poll.
`fio_io_last_tick_time()` is the cached wall-clock millisecond timestamp,
useful for approximate log and HTTP date values.
---
## Protocols
```c
typedef struct fio_io_protocol_s fio_io_protocol_s;
struct fio_io_protocol_s {
struct { /* reserved; initialize to zero */ } reserved;
void (*on_attach)(fio_io_s *io);
void (*on_data)(fio_io_s *io);
void (*on_ready)(fio_io_s *io);
void (*on_shutdown)(fio_io_s *io);
void (*on_timeout)(fio_io_s *io);
void (*on_close)(void *iobuf, void *udata);
void (*on_pubsub)(fio_pubsub_msg_s *msg);
void (*on_user1)(fio_io_s *io, void *user_data);
void (*on_user2)(fio_io_s *io, void *user_data);
void (*on_user3)(fio_io_s *io, void *user_data);
void (*on_reserved)(fio_io_s *io, void *user_data);
fio_io_functions_s io_functions;
uint32_t timeout;
uint32_t buffer_size;
};
```
Protocol structs define connection behavior. They are normally static/global and
shared by many IO handles. Initialize the full struct to zero and never mutate
`reserved`.
Callback flow:
1. `on_attach` after an IO is attached to the protocol.
2. `on_data` when incoming data can be read.
3. `on_ready` after all pending writes drain.
4. `on_timeout` when the connection timeout is reached.
5. `on_shutdown` immediately before reactor shutdown closes the connection.
6. `on_close(iobuf, udata)` after the connection is closed.
All protocol callbacks return `void`. Use `fio_io_close`,
`fio_io_close_now`, or protocol state to control what happens next. Set
`on_timeout = fio_io_touch` for connections where idle timeout should be
ignored.
`timeout` is in milliseconds, capped by `FIO_IO_TIMEOUT_MAX`; `0` means the
maximum/default. `buffer_size` controls the per-connection protocol buffer
returned by `fio_io_buffer`.
### Iterating protocol IOs
```c
size_t fio_io_protocol_each(fio_io_protocol_s *protocol,
void (*task)(fio_io_s *, void *udata2),
void *udata2);
```
Runs `task` for each IO using `protocol`. Call only from the main IO thread;
use `fio_io_defer` when scheduling from another thread.
---
## Transport Functions / TLS Hooks
```c
typedef struct fio_io_functions_s fio_io_functions_s;
struct fio_io_functions_s {
void *(*build_context)(fio_io_tls_s *tls, uint8_t is_client);
void (*free_context)(void *context);
void (*start)(fio_io_s *io);
ssize_t (*read)(fio_socket_i fd, void *buf, size_t len, void *context);
ssize_t (*write)(fio_socket_i fd, const void *buf, size_t len, void *context);
int (*flush)(fio_socket_i fd, void *context);
void (*finish)(fio_socket_i fd, void *context);
void (*cleanup)(void *context);
};
```
The vtable lets a protocol replace plain socket IO with a transport layer such
as TLS. Functions receive the file descriptor but must not keep it or defer fd
operations; the `fio_io_s` handle is the long-lived identity.
Return conventions:
- `read` behaves like non-blocking `read(2)`.
- `write` returns plaintext bytes accepted (`N > 0`), `0` for zero-length
input, or `-1` with `errno` set. If data was transformed (for example,
encrypted), return success even if the underlying socket later blocks; use
`flush` for pending transformed bytes.
- `flush` returns `0` only when all internal output is empty. Non-zero (`N > 0`
or `-1`) means pending data remains and the reactor should keep watching for
writability.
- `finish` runs before closing after output is sent; `cleanup` releases the
per-connection transport context after close.
Set default TLS transport functions before the reactor starts:
```c
fio_io_functions_s fio_io_tls_default_functions(fio_io_functions_s *funcs);
```
Passing `NULL` returns the current default. Passing a pointer sets a new
default and returns the selected functions.
---
## Listening and Connecting
### `fio_io_listen`
```c
typedef struct fio_io_listen_args_s {
const char *url;
fio_io_protocol_s *protocol;
void *udata;
fio_io_tls_s *tls;
void (*on_start)(fio_io_protocol_s *protocol, void *udata);
void (*on_stop)(fio_io_protocol_s *protocol, void *udata);
fio_io_async_s *queue_for_accept;
uint8_t on_root;
uint8_t hide_from_log;
} fio_io_listen_args_s;
fio_io_listener_s *fio_io_listen(fio_io_listen_args_s args);
#define fio_io_listen(...) fio_io_listen((fio_io_listen_args_s){__VA_ARGS__})
```
Creates a network listener and returns a self-destructible listener handle, or
`NULL` on error. The default URL is `tcp://0.0.0.0:3000`.
Call it before `fio_io_start()` for normal server setup. The header notes that
this schedules a task and should not be called from `PRE_START` or `ON_START`
state callbacks.
TLS can be supplied directly with `.tls` or inferred from the URL query:
```c
fio_io_listen(.url = "0.0.0.0:3000/?tls", .protocol = &MY_PROTOCOL);
fio_io_listen(.url = "0.0.0.0:3000/?tls=./certs/", .protocol = &MY_PROTOCOL);
fio_io_listen(.url = "0.0.0.0:3000/?key=./key.pem&cert=./cert.pem",
.protocol = &MY_PROTOCOL);
```
`.tls` ownership is moved to the listener. If you need to share a TLS settings
object, duplicate it first with `fio_io_tls_dup` and free your own references
with `fio_io_tls_free`.
Listener helpers:
```c
void fio_io_listen_stop(fio_io_listener_s *l);
fio_io_protocol_s *fio_io_listener_protocol(fio_io_listener_s *l);
void *fio_io_listener_udata(fio_io_listener_s *l);
void *fio_io_listener_udata_set(fio_io_listener_s *l, void *new_udata);
fio_buf_info_s fio_io_listener_url(fio_io_listener_s *l);
int fio_io_listener_is_tls(fio_io_listener_s *l);
```
### `fio_io_connect`
```c
typedef struct {
const char *url;
fio_io_protocol_s *protocol;
void (*on_failed)(fio_io_protocol_s *protocol, void *udata);
void *udata;
fio_io_tls_s *tls;
uint32_t timeout;
} fio_io_connect_args_s;
fio_io_s *fio_io_connect(fio_io_connect_args_s args);
#define fio_io_connect(url_, ...) \
fio_io_connect((fio_io_connect_args_s){.url = url_, __VA_ARGS__})
```
Connects to `url` as a client and returns the IO handle or `NULL`. The URL may
contain TLS hints. `timeout` defaults to 30 seconds. `on_failed` is the cleanup
hook for failed connection attempts; established connections use the protocol
callbacks.
---
## IO Handles and Operations
```c
fio_io_s *fio_io_attach_fd(fio_socket_i fd,
fio_io_protocol_s *protocol,
void *udata,
void *tls);
fio_io_protocol_s *fio_io_protocol_set(fio_io_s *io,
fio_io_protocol_s *protocol);
fio_io_protocol_s *fio_io_protocol(fio_io_s *io);
void *fio_io_buffer(fio_io_s *io);
size_t fio_io_buffer_len(fio_io_s *io);
void *fio_io_udata_set(fio_io_s *io, void *udata);
void *fio_io_udata(fio_io_s *io);
void *fio_io_tls_set(fio_io_s *io, void *tls);
void *fio_io_tls(fio_io_s *io);
fio_socket_i fio_io_fd(fio_io_s *io);
void fio_io_touch(fio_io_s *io);
size_t fio_io_read(fio_io_s *io, void *buf, size_t len);
void fio_io_close(fio_io_s *io);
void fio_io_close_now(fio_io_s *io);
fio_io_s *fio_io_dup(fio_io_s *io);
void fio_io_free(fio_io_s *io);
void fio_io_suspend(fio_io_s *io);
void fio_io_unsuspend(fio_io_s *io);
int fio_io_is_suspended(fio_io_s *io);
int fio_io_is_open(fio_io_s *io);
size_t fio_io_backlog(fio_io_s *io);
void fio_io_noop(fio_io_s *io);
```
`fio_io_attach_fd` adopts a valid socket into the reactor. It returns `NULL` on
error. The returned pointer must not be used arbitrarily; IO handles are valid
inside proper callbacks and scheduled tasks. If code must keep a handle outside
that context, call `fio_io_dup` and later `fio_io_free`. These two functions are
thread-safe.
`fio_io_protocol_set` installs a new protocol. `NULL` is a valid "only-write"
protocol. The accessor may temporarily return the old protocol while the change
is being attached.
`fio_io_read` returns bytes read. `0` is not EOF by itself; it can mean no data
was available on the non-blocking socket. Use close callbacks for final cleanup.
`fio_io_close` closes after scheduled data is sent. `fio_io_close_now` closes as
soon as possible. `fio_io_is_open` returns 1 only while open and not marked for
closure: it returns 0 immediately after `fio_io_close`, even while previously
scheduled output is draining. `fio_io_suspend` / `fio_io_unsuspend` control future
`on_data` delivery, and `fio_io_backlog` reports the approximate outgoing byte
count.
### Writing
```c
typedef struct {
void *buf;
intptr_t fd;
size_t len;
size_t offset;
void (*dealloc)(void *);
uint8_t copy;
} fio_io_write_args_s;
void fio_io_write2(fio_io_s *io, fio_io_write_args_s args);
#define fio_io_write2(io, ...) \
fio_io_write2(io, (fio_io_write_args_s){__VA_ARGS__})
#define fio_io_write(io, buf_, len_) \
fio_io_write2(io, .buf = (buf_), .len = (len_), .copy = 1)
#define fio_io_sendfile(io, source_fd, offset_, bytes) \
fio_io_write2((io), .fd = (source_fd), .offset = (size_t)(offset_), .len = (bytes))
```
`fio_io_write2` schedules buffered output. If `.buf` is used and `.copy` is
non-zero, the data is copied immediately. If `.copy == 0` and `.dealloc` is set,
the IO layer takes ownership of the buffer and calls `dealloc` later. If
`.dealloc == NULL`, the buffer is not freed by the IO layer.
For file output, pass `.fd`; `.len == 0` means send the whole file. The
`fio_io_sendfile` helper closes `source_fd` after sending or on error.
---
## Scheduling Tasks and Timers
```c
void fio_io_defer(void (*task)(void *, void *), void *udata1, void *udata2);
void fio_io_run_every(fio_timer_schedule_args_s args);
#define fio_io_run_every(...) \
fio_io_run_every((fio_timer_schedule_args_s){__VA_ARGS__})
fio_queue_s *fio_io_queue(void);
```
`fio_io_defer` schedules a task on the IO reactor queue and is thread-safe.
Use it to move work from other threads back to the IO thread.
`fio_io_run_every` schedules a timer. It uses `fio_timer_schedule_args_s` from
the queue/timer API:
- `fn` returns non-zero to stop the timer.
- `udata1` and `udata2` are passed to callbacks.
- `on_stop` runs when the timer ends.
- `every` is the interval in milliseconds.
- `repetitions` is the number of runs; `-1` means indefinitely.
`fio_io_queue()` returns the reactor queue.
---
## Connection Environment
The IO environment links named objects to a connection lifetime. When the IO is
closed, stored `on_close` callbacks run automatically.
```c
typedef struct {
intptr_t type;
fio_buf_info_s name;
void *udata;
void (*on_close)(void *data);
uint8_t const_name;
} fio_io_env_set_args_s;
typedef struct {
intptr_t type;
fio_buf_info_s name;
} fio_io_env_get_args_s;
void *fio_io_env_get(fio_io_s *io, fio_io_env_get_args_s args);
void fio_io_env_set(fio_io_s *io, fio_io_env_set_args_s args);
int fio_io_env_unset(fio_io_s *io, fio_io_env_get_args_s args);
int fio_io_env_remove(fio_io_s *io, fio_io_env_get_args_s args);
#define fio_io_env_get(io, ...) \
fio_io_env_get(io, (fio_io_env_get_args_s){__VA_ARGS__})
#define fio_io_env_set(io, ...) \
fio_io_env_set(io, (fio_io_env_set_args_s){__VA_ARGS__})
#define fio_io_env_unset(io, ...) \
fio_io_env_unset(io, (fio_io_env_get_args_s){__VA_ARGS__})
#define fio_io_env_remove(io, ...) \
fio_io_env_remove(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
`type` and `name` together identify an entry. Negative `type` values are
reserved. If `const_name` is set, the name string must outlive the environment.
If `io == NULL`, entries are stored in the global environment; their `on_close`
callbacks run when the process exits. `unset` detaches without calling
`on_close`; `remove` detaches and calls `on_close` as if the connection closed.
---
## TLS Settings Helpers
```c
fio_io_tls_s *fio_io_tls_new(void);
fio_io_tls_s *fio_io_tls_from_url(fio_io_tls_s *target_or_null, fio_url_s url);
fio_io_tls_s *fio_io_tls_dup(fio_io_tls_s *tls);
void fio_io_tls_free(fio_io_tls_s *tls);
fio_io_tls_s *fio_io_tls_cert_add(fio_io_tls_s *tls,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
fio_io_tls_s *fio_io_tls_alpn_add(fio_io_tls_s *tls,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int fio_io_tls_alpn_select(fio_io_tls_s *tls,
const char *protocol_name,
size_t name_length,
fio_io_s *io);
fio_io_tls_s *fio_io_tls_trust_add(fio_io_tls_s *tls,
const char *public_cert_file);
uintptr_t fio_io_tls_cert_count(fio_io_tls_s *tls);
uintptr_t fio_io_tls_alpn_count(fio_io_tls_s *tls);
uintptr_t fio_io_tls_trust_count(fio_io_tls_s *tls);
```
`fio_io_tls_s` stores backend-neutral TLS instructions. Add certificates for
SNI, ALPN protocol callbacks, and trusted certificates / CA stores. Passing
`NULL` certificate paths can request a backend-provided self-signed certificate;
passing `NULL` to `fio_io_tls_trust_add` asks the backend to use system trust.
Backend behavior is documented in the TLS backend docs.
`fio_io_tls_alpn_add` silently ignores a `NULL` protocol name and replaces a
`NULL` callback with a no-op. The first ALPN protocol added is the default.
### Iterating TLS settings
```c
typedef struct fio_io_tls_each_s {
fio_io_tls_s *tls;
void *udata;
void *udata2;
int (*each_cert)(struct fio_io_tls_each_s *,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
int (*each_alpn)(struct fio_io_tls_each_s *,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int (*each_trust)(struct fio_io_tls_each_s *, const char *public_cert_file);
} fio_io_tls_each_s;
int fio_io_tls_each(fio_io_tls_each_s args);
#define fio_io_tls_each(tls_, ...) \
fio_io_tls_each(((fio_io_tls_each_s){.tls = tls_, __VA_ARGS__}))
```
Transport backends use `fio_io_tls_each` to consume the stored instructions.
Iterator callbacks return `int`; check the backend using them for any additional
meaning assigned to non-zero return values.
### Peer certificate inspection (mTLS)
```c
int fio_io_peer_info_next(fio_io_s *io, fio_x509_cert_s *dest);
```
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: a server context verifies (and requires) client certificates,
a client context verifies the server certificate. An empty trust list means
no peer verification; passing `NULL` to `fio_io_tls_trust_add` selects the
system trust store.
Once the handshake completed, iterate the peer's certificate chain (leaf
first) to implement application level client certificate authentication and
authorization:
```c
fio_x509_cert_s cert = {0}; /* zeroed = new loop */
while (fio_io_peer_info_next(io, &cert) == 0) {
if (!cert.verified)
continue; /* chain failed TLS-level verification (or was skipped) */
/* authorize by identity, e.g.: cert.cn,
* cert.subject, or pin cert.fingerprint (32-byte SHA-256) */
}
```
The iterator is stateless — the position is identified from `dest` alone: a
zeroed `dest` (`der.buf == NULL`) starts a new loop; otherwise iteration
continues at `dest->chain_index + 1`. To restart a loop, zero the struct.
Multiple loops may iterate the same connection concurrently.
Iteration is capped at 128 certificates (`chain_index` 0..127) as a
deep-nesting / DoS guard — longer chains end the loop with -1.
Passing a `NULL` `dest` returns -1. The function returns
-1 when the iteration is done or when peer information is unavailable (no
TLS, handshake incomplete, peer sent no certificate, or the X509 module
missing).
Each call parses the next certificate into a `fio_x509_cert_s` (defined in
the X509 module, `156 x509.h`). All certificate fields point directly into
memory owned by the TLS backend — nothing is copied or allocated. The views
stay valid until the next `fio_io_peer_info_next` call (on ANY connection)
or connection close, whichever comes first; copy anything that must outlive
the loop.
The `verified` flag reflects the TLS backend's verification of the whole
chain and is identical for every certificate in the chain. Connections that
skipped verification (empty trust list) report `verified == 0`.
---
## Async Worker Queues
```c
typedef struct fio_io_async_s fio_io_async_s;
#define FIO_IO_ASYN_INIT ((fio_io_async_s){0})
fio_queue_s *fio_io_async_queue(fio_io_async_s *q);
void fio_io_async_attach(fio_io_async_s *q, uint32_t threads);
#define fio_io_async(q_, ...) fio_queue_push((q_)->q, __VA_ARGS__)
void fio_io_async_every(fio_io_async_s *q, fio_timer_schedule_args_s args);
#define fio_io_async_every(async, ...) \
fio_io_async_every(async, (fio_timer_schedule_args_s){__VA_ARGS__})
```
Async queues are for non-IO work that must not block the reactor. Allocate the
`fio_io_async_s` object with static or otherwise reactor-long lifetime and
initialize it with `FIO_IO_ASYN_INIT` or zeroes before attaching it.
```c
static fio_io_async_s SLOW_TASKS = FIO_IO_ASYN_INIT;
int main(void) {
fio_io_async_attach(&SLOW_TASKS, 32);
fio_io_start(0);
}
```
The queue starts and stops with the IO reactor. Use `fio_io_async_queue` when a
raw `fio_queue_s *` is needed, `fio_io_async` to push tasks, and
`fio_io_async_every` for timers on that async queue.
---
## Minimal Echo Server
```c
#define FIO_IO
#include "fio-stl/include.h"
static void echo_on_data(fio_io_s *io) {
char buf[4096];
size_t len;
while ((len = fio_io_read(io, buf, sizeof(buf))))
fio_io_write(io, buf, len); /* copies stack buffer */
}
static void echo_on_timeout(fio_io_s *io) { fio_io_close(io); }
static fio_io_protocol_s ECHO = {
.on_data = echo_on_data,
.on_timeout = echo_on_timeout,
.timeout = 30000,
};
int main(void) {
fio_io_listen(.url = "tcp://0.0.0.0:3000", .protocol = &ECHO);
fio_io_start(0);
}
```
---
## Common Lifetime Rules
- Treat `fio_io_s *` as callback/task scoped unless you hold a reference with
`fio_io_dup`; release it with `fio_io_free`.
- Do not keep raw file descriptors received by transport callbacks.
- Protocol objects should be zero-initialized and live at least as long as any
IO that uses them.
- Listener `.tls` is moved to the listener; duplicate TLS settings before
sharing them elsewhere.
- `fio_io_write` copies the buffer; `fio_io_write2` can copy, borrow, or take
ownership depending on `.copy` and `.dealloc`.
- Environment `on_close` callbacks are the preferred cleanup point for objects
tied to a connection lifetime.
# IPC — Inter-Process Communication (404 ipc.h)
```c
#define FIO_IPC
#include FIO_INCLUDE_FILE
```
IPC is the messaging backbone of the facil.io worker/master model. Workers
fork from a master process. The master keeps listeners, manages state, and
handles anything that needs a single authoritative view of the world. Workers
handle client connections. IPC bridges the two: a worker sends a message to
the master (or to every process in the cluster), the master executes it and
optionally streams replies back.
All messages are encrypted in transit with ChaCha20-Poly1305 AEAD, keyed from
the process secret. Local IPC runs over a Unix socket (or auto-generated TCP
socket). Multi-machine RPC extends the same protocol over TCP with optional
UDP peer discovery.
See [./400 io-overview.md](./400 io-overview.md) for where IPC sits in the
full IO stack.
---
## Worker/Master Model
```
master process
├── owns IPC listener socket
├── executes messages sent by workers
├── forwards cluster/broadcast messages to other machines
└── worker process × N
├── each has its own IO reactor loop
├── connects back to master via IPC socket on startup
└── sends fio_ipc_* messages to reach master or peers
```
The master is always the hub. Workers can only talk *through* the master —
there is no direct worker-to-worker channel. On shutdown the master sends a
local broadcast instructing all workers to stop.
---
## Configuration Macros
Define before including the header to override defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_IPC_URL_MAX_LENGTH` | `1024` | Max IPC socket URL length |
| `FIO_IPC_MAX_LENGTH` | `128 MiB` | Max message payload size |
---
## Types
### `fio_ipc_s` — the message
```c
typedef struct fio_ipc_s {
fio_io_s *from; /* originating IO (set by receiver; not transmitted) */
/* -- wire format starts here -- */
uint32_t len; /* data[] byte count (AAD - authenticated) */
uint16_t flags; /* user-settable flags (AAD - authenticated) */
uint16_t routing_flags; /* internal routing (AAD - authenticated) */
uint64_t timestamp; /* millisecond tick — part of nonce (unencrypted) */
uint64_t id; /* random 64-bit value — part of nonce (unencrypted) */
union {
void (*call)(struct fio_ipc_s *); /* function to call (local IPC; encrypted) */
uint32_t opcode; /* registered op-code (cluster RPC; encrypted)*/
};
void (*on_reply)(struct fio_ipc_s *); /* run on caller when reply arrives (encrypted) */
void (*on_done)(struct fio_ipc_s *); /* run on caller when last reply arrives (encrypted) */
void *udata; /* caller-side opaque pointer (encrypted) */
char data[]; /* payload + 16-byte Poly1305 MAC at tail (encrypted) */
} fio_ipc_s;
```
Messages are reference-counted. Everything from `call` onward (including the
payload) is encrypted on the wire. `from`, `timestamp`, and `id` are unencrypted
and unauthenticated (they form the nonce). `len`, `flags`, and `routing_flags`
are unencrypted but authenticated via AEAD.
### `fio_ipc_args_s` — construction arguments
```c
typedef struct {
void (*call)(fio_ipc_s *); /* function to run on target (required for local IPC) */
void (*on_reply)(fio_ipc_s *); /* called on caller when a reply arrives */
void (*on_done)(fio_ipc_s *); /* called on caller when final reply arrives */
fio_io_s *exclude; /* IO to exclude from delivery (or FIO_IPC_EXCLUDE_SELF) */
uint64_t timestamp; /* force timestamp; 0 = use reactor tick */
uint64_t id; /* force message ID; 0 = random */
uint32_t opcode; /* registered op-code; replaces call if non-zero */
uint16_t flags; /* user-settable flags */
bool cluster; /* deliver to remote machines in cluster */
bool workers; /* deliver to master + all local workers */
void *udata; /* opaque pointer for reply callbacks */
fio_buf_info_s *data; /* payload (use FIO_IPC_DATA macro) */
} fio_ipc_args_s;
```
### `fio_ipc_opcode_s` — op-code registration
```c
typedef struct fio_ipc_opcode_s {
uint32_t opcode; /* unique non-zero value */
void (*call)(struct fio_ipc_s *); /* handler */
void (*on_reply)(struct fio_ipc_s *); /* reply callback */
void (*on_done)(struct fio_ipc_s *); /* final reply callback */
void *udata; /* provided to handler via ipc->udata */
} fio_ipc_opcode_s;
```
### `fio_ipc_reply_args_s` — reply arguments
```c
typedef struct {
fio_ipc_s *ipc; /* original request (required) */
fio_buf_info_s *data; /* reply payload */
uint64_t timestamp; /* 0 = current tick */
uint64_t id; /* 0 = use original request ID */
uint16_t flags; /* 0 = use original request flags */
uint8_t done; /* set to 1 on last reply (triggers on_done on caller) */
uint8_t flags_set; /* set to 1 to allow flags=0 override */
} fio_ipc_reply_args_s;
```
---
## Helper Macro: `FIO_IPC_DATA`
```c
#define FIO_IPC_DATA(...) \
(fio_buf_info_s[]){ __VA_ARGS__, { .len = ((size_t)-1) } }
```
Composes multiple buffers into a single message without intermediate
allocation. Iteration stops at the first entry with `len == (size_t)-1`.
```c
uint32_t seq = 42;
const char *text = "hello";
fio_ipc_call(.call = my_handler,
.data = FIO_IPC_DATA(
FIO_BUF_INFO2(&seq, sizeof(seq)),
FIO_BUF_INFO1((char *)text)));
/* seq and text are copied immediately — stack safe */
```
---
## Delivery Macros
All four macros call `fio_ipc_new(...)` then `fio_ipc_send(...)`. They accept
named fields from `fio_ipc_args_s`.
| Macro | Delivered to |
|-------|-------------|
| `fio_ipc_call(...)` | master only |
| `fio_ipc_local(...)` | master + all local workers |
| `fio_ipc_cluster(...)` | master process on every machine (local and remote) |
| `fio_ipc_broadcast(...)` | master + workers on every machine (local and remote) |
```c
/* worker → master */
#define fio_ipc_call(...) fio_ipc_send(fio_ipc_new(__VA_ARGS__))
/* all local processes */
#define fio_ipc_local(...) fio_ipc_send(fio_ipc_new(.workers = 1, __VA_ARGS__))
/* remote masters only */
#define fio_ipc_cluster(...) fio_ipc_send(fio_ipc_new(.cluster = 1, __VA_ARGS__))
/* everywhere */
#define fio_ipc_broadcast(...) fio_ipc_send(fio_ipc_new(.workers = 1, .cluster = 1, __VA_ARGS__))
```
`fio_ipc_cluster` and `fio_ipc_broadcast` require an `opcode` (not a function
pointer) because function pointers are meaningless across machine boundaries.
Use `.exclude = FIO_IPC_EXCLUDE_SELF` to skip the calling process when
broadcasting locally.
**`FIO_IPC_EXCLUDE_SELF`**
```c
#define FIO_IPC_EXCLUDE_SELF ((fio_io_s *)((char *)-1LL))
```
Pass as `.exclude` in `fio_ipc_args_s`, or set `ipc->from = FIO_IPC_EXCLUDE_SELF`
before calling `fio_ipc_send`.
---
## Quick Examples
### Worker calls master, master replies
```c
/* runs on master */
void on_master(fio_ipc_s *msg) {
printf("master got: %.*s\n", (int)msg->len, msg->data);
fio_ipc_reply(msg,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"pong")),
.done = 1);
}
/* runs on calling worker when reply arrives */
void on_reply(fio_ipc_s *msg) {
printf("worker got reply: %.*s\n", (int)msg->len, msg->data);
}
/* somewhere in a worker callback */
fio_ipc_call(.call = on_master,
.on_done = on_reply,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"ping")));
```
### Local broadcast (skip self)
```c
void on_notify(fio_ipc_s *msg) {
printf("[%d] notified: %.*s\n", fio_io_pid(), (int)msg->len, msg->data);
}
fio_ipc_local(.call = on_notify,
.exclude = FIO_IPC_EXCLUDE_SELF,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"config-reload")));
```
### Cluster-wide op-code broadcast
```c
#define OP_CACHE_BUST 1
void on_cache_bust(fio_ipc_s *msg) {
cache_invalidate(msg->data, msg->len);
}
/* register before fio_io_start */
fio_ipc_opcode_register(.opcode = OP_CACHE_BUST, .call = on_cache_bust);
fio_ipc_cluster_listen(9000); /* requires SECRET env var */
fio_io_start(4);
/* later, from any process */
fio_ipc_broadcast(.opcode = OP_CACHE_BUST,
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"key123")));
```
---
## Reply: `fio_ipc_reply`
```c
SFUNC void fio_ipc_reply(fio_ipc_reply_args_s args);
/* shadowing macro — first arg is the original request */
#define fio_ipc_reply(r, ...) \
fio_ipc_reply((fio_ipc_reply_args_s){ .ipc = (r), __VA_ARGS__ })
```
Send one or more replies from the master back to the caller. Set `.done = 1`
on the final reply to trigger the caller's `on_done` callback. Replies inherit
`call`, `on_reply`, `on_done`, and `udata` from the original request.
Streaming replies:
```c
void stream_handler(fio_ipc_s *msg) {
for (int i = 0; i < 5; ++i) {
char chunk[32];
int n = snprintf(chunk, sizeof(chunk), "chunk%d", i);
fio_ipc_reply(msg,
.data = FIO_IPC_DATA(FIO_BUF_INFO2(chunk, (size_t)n)),
.done = (i == 4));
}
}
```
---
## Op-Code Registration
Op-codes let you perform cluster-wide RPC without transmitting function
pointers (which differ per binary). Register before `fio_io_start`.
```c
SFUNC int fio_ipc_opcode_register(fio_ipc_opcode_s opcode);
/* named-arg macro */
#define fio_ipc_opcode_register(...) \
fio_ipc_opcode_register((fio_ipc_opcode_s){ __VA_ARGS__ })
/** Returns registered op-code or NULL. */
SFUNC const fio_ipc_opcode_s *fio_ipc_opcode(uint32_t opcode);
```
- Op-codes must be non-zero `uint32_t` values.
- Values `>= 0xFF000000` are reserved for internal use.
- Pass `call = NULL` to unregister.
- Must be called before `fio_io_start()` (not thread-safe after that).
- Returns 0 on success, -1 on error.
```c
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = on_sync, .udata = ctx);
/* look up */
const fio_ipc_opcode_s *op = fio_ipc_opcode(OP_SYNC);
/* remove */
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = NULL);
```
---
## IPC URL
The IPC socket URL is auto-generated at startup (`unix://fio_tmp_.sock`
in `$TMPDIR`). Override before `fio_io_start` on the master:
```c
/** Returns current IPC socket URL. */
SFUNC const char *fio_ipc_url(void);
/**
* Sets IPC socket URL. Master only, before IO reactor starts.
* Pass NULL to regenerate. 'X' / '#' characters in the URL are replaced with
* random hex digits. Returns 0 on success, -1 on error.
*/
SFUNC int fio_ipc_url_set(const char *url);
```
```c
fio_ipc_url_set("unix:///var/run/myapp-XXXX.sock"); /* X replaced randomly */
fio_ipc_url_set(NULL); /* auto-generate */
printf("%s\n", fio_ipc_url());
```
---
## Multi-Machine Cluster (RPC)
```c
/**
* Listen for cluster peers on port. Auto-discovers peers via UDP broadcast.
* Requires a shared SECRET environment variable (non-random secret).
* Returns listener handle or NULL if disabled.
*/
SFUNC fio_io_listener_s *fio_ipc_cluster_listen(uint16_t port);
/** Manually connect to a cluster peer (usually unnecessary). */
SFUNC void fio_ipc_cluster_connect(const char *url);
/** Returns last port passed to fio_ipc_cluster_listen / connect, or 0. */
SFUNC uint16_t fio_ipc_cluster_port(void);
```
- Call `fio_ipc_cluster_listen` from the master before or after
`fio_io_start`. It opens a TCP listener and a UDP broadcast socket on the
same port for automatic peer discovery.
- Uses the environment's shared secret for encryption (ChaCha20-Poly1305,
no forward secrecy). Set `SECRET=` across all instances.
- All instances receive all `cluster` / `broadcast` messages.
- `fio_ipc_cluster_connect` lets you manually reach peers on other subnets
where UDP broadcast doesn't reach.
```c
/* Typical setup */
fio_ipc_opcode_register(.opcode = OP_SYNC, .call = on_sync);
fio_ipc_cluster_listen(9000); /* returns NULL/no-op if SECRET is unset or random */
fio_io_start(4);
```
---
## Core Lifetime API
These are used internally by the delivery macros and are available for
advanced use (e.g., constructing messages for local execution, or async
message handling).
```c
/** Create a message without sending it. */
SFUNC fio_ipc_s *fio_ipc_new(fio_ipc_args_s args);
#define fio_ipc_new(...) fio_ipc_new((fio_ipc_args_s){ __VA_ARGS__ })
/** Encrypt, route, and free the message. Takes ownership. */
SFUNC void fio_ipc_send(fio_ipc_s *ipc);
/** Encrypt and send directly to a specific IO. Takes ownership. */
SFUNC void fio_ipc_send_to(fio_io_s *to, fio_ipc_s *ipc);
/** Increment reference count. */
SFUNC fio_ipc_s *fio_ipc_dup(fio_ipc_s *msg);
/** Decrement reference count; destroy when zero. */
SFUNC void fio_ipc_free(fio_ipc_s *msg);
/** Release the msg->from IO reference (call if storing msg beyond callback). */
SFUNC void fio_ipc_detach(fio_ipc_s *msg);
/** Set a one-shot callback invoked when sending is complete.
The callback receives ownership of the decrypted message and must free it. */
SFUNC void fio_ipc_after_send(fio_ipc_s *ipc,
void (*fn)(fio_ipc_s *, void *),
void *udata);
```
Typical async pattern:
```c
void my_handler(fio_ipc_s *msg) {
fio_ipc_s *ref = fio_ipc_dup(msg); /* keep alive past callback return */
fio_ipc_detach(ref); /* release the from-IO ref if unneeded */
do_async_work(ref); /* stored elsewhere */
}
void async_done(fio_ipc_s *msg) {
process(msg->data, msg->len);
fio_ipc_free(msg);
}
```
---
## Encryption
```c
/** Encrypt a message in-place (idempotent). Called automatically by fio_ipc_send. */
SFUNC void fio_ipc_encrypt(fio_ipc_s *m);
/** Decrypt a message in-place. Returns 0 on success, non-zero on MAC failure. */
FIO_IFUNC int fio_ipc_decrypt(fio_ipc_s *m);
```
ChaCha20-Poly1305 AEAD. Key derived from the shared process secret plus the
message's `id` and `timestamp` fields (which form the nonce). `len`, `flags`,
and `routing_flags` are authenticated but unencrypted. Everything else
(`call`/`opcode`, `on_reply`, `on_done`, `udata`, `data`) is encrypted.
Decryption failure logs a security warning and the connection is closed.
Only processes sharing the same secret can communicate.
---
## Routing Flags
`routing_flags` is internal — do not edit manually. Available for inspection:
```c
#define FIO_IPC_FLAG_ENCRYPTED ((uint16_t)1 << 0) /* message is encrypted */
#define FIO_IPC_FLAG_DONE ((uint16_t)1 << 1) /* final reply */
#define FIO_IPC_FLAG_OPCODE ((uint16_t)1 << 2) /* route via op-code */
#define FIO_IPC_FLAG_WORKERS ((uint16_t)1 << 3) /* deliver to workers too */
#define FIO_IPC_FLAG_CLUSTER ((uint16_t)1 << 4) /* deliver to remote machines */
#define FIO_IPC_FLAG_REPLY ((uint16_t)1 << 5) /* this is a reply */
#define FIO_IPC_FLAG_PING ((uint16_t)1 << 6) /* internal keepalive */
/** Test a flag: returns flag value if set, 0 otherwise. */
#define FIO_IPC_FLAG_TEST(msg, flag) (((msg)->routing_flags & (flag)) == (flag))
```
---
## Keepalive
IPC and cluster connections use an automatic ping/pong protocol. On the first
timeout the system sends a ping; if no pong arrives before the next timeout
the connection is closed. Ping frames are never delivered to user callbacks.
Default timeouts (set in the protocol initializer):
| Connection type | Timeout |
|-----------------|---------|
| Local IPC (master ↔ worker) | ~6 minutes |
| Cluster RPC (machine ↔ machine) | ~55 seconds |
---
## Memory Management
- Messages are reference-counted (`fio_ipc_dup` / `fio_ipc_free`).
- Callbacks receive a message pointer valid for the duration of the callback.
The system frees it automatically after the callback returns.
- Call `fio_ipc_dup` to keep a message alive beyond the callback, then
`fio_ipc_free` when done.
- Data passed via `FIO_IPC_DATA` is copied into the message immediately —
source buffers may be stack-allocated or freed right after the call.
---
## Thread Safety
- `fio_ipc_call`, `fio_ipc_local`, `fio_ipc_cluster`, `fio_ipc_broadcast`,
and `fio_ipc_reply` are safe to call from any thread.
- `fio_ipc_dup` / `fio_ipc_free` are thread-safe.
- `fio_ipc_opcode_register` is **not** thread-safe after `fio_io_start`.
- `fio_ipc_url_set` must be called on the master before `fio_io_start`.
# OpenSSL TLS Backend
```c
#define FIO_IO
#include "fio-stl/include.h"
```
OpenSSL 3.x IO-layer TLS backend for the facil.io reactor. When OpenSSL is
available at build time this module auto-registers itself as the default TLS
transport. You don't have to call anything — just define `FIO_IO` and link
against OpenSSL 3.x.
For IO-layer TLS context overview see [./400 io-overview.md](./400 io-overview.md).
For the sibling native TLS 1.3 backend see [./405 tls13.md](./405 tls13.md) *(planned)*.
For standalone TLS 1.3 crypto (key schedule, record layer) see [./190 tls13.md](./190 tls13.md).
---
## Activation
The module compiles automatically when all of the following are true:
| Condition | Notes |
|-----------|-------|
| `FIO_IO` is defined | pulls in the IO reactor |
| `FIO_NO_TLS` is **not** defined | opt-out guard |
| OpenSSL 3.x headers are present | `HAVE_OPENSSL` or `__has_include("openssl/ssl.h")` |
If OpenSSL headers are found but the version is older than 3.x
(`OPENSSL_VERSION_MAJOR < 3`), the module emits a compiler warning and
makes `fio_openssl_io_functions()` return `fio_io_tls_default_functions(NULL)`
(the current default, usually a no-op). Everything compiles; TLS simply does nothing.
A module-level constructor runs before `main()`:
- Initialises the custom BIO methods.
- Calls `fio_io_tls_default_functions(&openssl_io_funcs)` so every subsequent
`fio_io_listen` / `fio_io_connect` call with a `tls` argument uses OpenSSL.
- Registers a `SIGPIPE` monitor (OpenSSL can trigger SIGPIPE on broken sockets).
- Registers a `FIO_CALL_AT_EXIT` cleanup for the custom BIO method objects.
---
## Public API
### `fio_openssl_io_functions`
```c
fio_io_functions_s fio_openssl_io_functions(void);
```
Returns the `fio_io_functions_s` vtable that wires OpenSSL into the IO reactor:
| Field | Role |
|-------|------|
| `build_context` | Converts `fio_io_tls_s` into an `SSL_CTX` wrapper |
| `free_context` | Deferred free of `SSL_CTX` and the `fio_io_tls_s` reference |
| `start` | Per-connection: `SSL_new`, custom BIO setup, initial handshake |
| `read` | Non-blocking decrypt: advances handshake if needed, then `SSL_read_ex` |
| `write` | Non-blocking encrypt: `SSL_write_ex` → encrypt → socket |
| `flush` | Sends any pending encrypted bytes from the internal buffer |
| `finish` | Sends TLS `close_notify` before the TCP close |
| `cleanup` | Frees the per-connection `SSL` object |
Normally you never call this directly — the constructor handles registration.
Use it explicitly only when overriding the default or building a custom setup:
```c
/* Override: switch back to OpenSSL after something else changed the default */
fio_io_functions_s openssl_funcs = fio_openssl_io_functions();
fio_io_tls_default_functions(&openssl_funcs);
```
Or to set it on a specific protocol without touching the global default
(the `io_functions` field lives on `fio_io_protocol_s`):
```c
fio_io_functions_s openssl_funcs = fio_openssl_io_functions();
MY_PROTOCOL.io_functions = openssl_funcs; /* per-protocol override */
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &MY_PROTOCOL,
.tls = tls);
```
---
## Configuring TLS — `fio_io_tls_s`
TLS parameters are held in a `fio_io_tls_s` object defined in `401 io api.h`.
Build one before calling `fio_io_listen` or `fio_io_connect`:
```c
/* Allocate (reference counted) */
fio_io_tls_s *tls = fio_io_tls_new();
/* Certificate — PEM files */
fio_io_tls_cert_add(tls,
"www.example.com", /* server_name (SNI) */
"cert.pem", /* public certificate or chain */
"key.pem", /* private key */
NULL); /* PEM password, or NULL */
/* ALPN protocol negotiation */
fio_io_tls_alpn_add(tls, "h2", on_http2_selected);
fio_io_tls_alpn_add(tls, "http/1.1", on_http1_selected);
/* Peer certificate verification */
fio_io_tls_trust_add(tls, NULL); /* use system trust store */
fio_io_tls_trust_add(tls, "ca.pem"); /* or a specific CA bundle */
/* Listen */
fio_io_listen(.url = "0.0.0.0:443",
.protocol = &MY_PROTOCOL,
.tls = tls);
fio_io_tls_free(tls); /* release your reference; the listener holds its own */
fio_io_start(0);
```
`fio_io_tls_s` is reference-counted (`fio_io_tls_dup` / `fio_io_tls_free`).
The backend duplicates the reference during `build_context`; freeing yours
after `fio_io_listen` is always safe.
### URL shorthand
The URL query string can also configure TLS without building the object
manually:
```c
fio_io_listen(.url = "0.0.0.0:443/?tls=./certs/", .protocol = &MY_PROTOCOL);
```
See `fio_io_tls_from_url` in `401 io api.h` for the query syntax.
---
## Certificates
### Loading from PEM files
When `public_cert_file` and `private_key_file` are both provided to
`fio_io_tls_cert_add`, the backend:
1. Sets a PEM password callback if `pk_password` is non-`NULL`.
2. Loads the certificate chain via `SSL_CTX_use_certificate_chain_file`.
3. Loads the private key via `SSL_CTX_use_PrivateKey_file` (PEM format).
4. Verifies key-cert consistency with `SSL_CTX_check_private_key`.
Errors are logged with `FIO_LOG_ERROR` and the context build fails.
### Self-signed fallback
When a **server** has no certificates configured (or `fio_io_tls_cert_add` is
called with both file arguments as `NULL`), the backend generates a self-signed
ECDSA P-256 certificate on the fly:
| Property | Value |
|----------|-------|
| Key algorithm | ECDSA P-256 (128-bit security ≈ RSA-3072) |
| Key generation time | ~10 ms |
| Signature | SHA-256 |
| Validity | 180 days |
| Serial number | 128-bit cryptographically random |
| X.509 extensions | Basic Constraints (`CA:FALSE`), Key Usage, Extended Key Usage (`serverAuth`), SAN |
The private key is generated once per process (thread-safe, lock-protected) and
freed at exit via a `FIO_CALL_AT_EXIT` callback.
Self-signed certificates are fine for development. Browsers will warn. Use a
CA-issued certificate (e.g. Let's Encrypt) in production.
---
## ALPN
Register protocols with `fio_io_tls_alpn_add` before listen/connect. The first
registered protocol is the preferred default.
When a client sends an ALPN extension, the backend walks the offered names in
order and picks the first that matches a registered protocol. On match it calls
`fio_io_tls_alpn_select`, which fires the `on_selected` callback for that
connection. If nothing matches, the handshake fails with a fatal alert.
Protocol names must be 1–255 bytes. The internal wire-format list is capped at
1 023 bytes total; more protocols than that will log an overflow error.
---
## Trust and Peer Verification
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: a client context verifies the server certificate, a server
context requests, requires, and verifies the client certificate (mutual TLS).
An empty trust list means **no peer verification**.
| Scenario | Behaviour |
|----------|-----------|
| `fio_io_tls_trust_add` called (any argument) | `SSL_VERIFY_PEER` enabled |
| `NULL` passed to `fio_io_tls_trust_add` | system trust store is loaded (`X509_STORE_set_default_paths`) |
| Trust configured, server mode | `SSL_VERIFY_PEER \| SSL_VERIFY_FAIL_IF_NO_PEER_CERT` (client certificate required) |
| No trust certs added, client mode | `SSL_VERIFY_NONE` + `FIO_LOG_SECURITY` warning |
| No trust certs added, server mode | `SSL_VERIFY_NONE` (no client certificate requested) |
After the handshake, inspect the peer's certificate chain with
`fio_io_peer_info_next(io, &info)` (see the IO API docs). Each call
re-encodes the next chain certificate into a fixed per-connection staging
buffer (no allocation) and parses it into a `fio_x509_cert_s`, exposing the
subject, issuer, SAN entries, public key, validity, and a SHA-256
fingerprint. `info.verified` reflects OpenSSL's chain verification result
(`SSL_get_verify_result`).
---
## SSL Context Modes
The `SSL_CTX` is configured for non-blocking use:
```
SSL_MODE_ENABLE_PARTIAL_WRITE — SSL_write may return before all data encrypted
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER — buffer pointer may differ between retries
SSL_MODE_RELEASE_BUFFERS — free idle 34 KB per-connection OpenSSL buffers
SSL_MODE_AUTO_RETRY — CLEARED (return immediately on WANT_READ/WRITE)
```
TLS 1.3 session tickets: 2 tickets are configured for session resumption
(saves ~30–50 % of handshake CPU on reconnects).
---
## Internal Design — Custom BIOs
Instead of `BIO_s_mem()` (which bounces data through a private `BUF_MEM`),
the backend uses custom `BIO_METHOD` objects that give OpenSSL direct access to
the per-connection buffers:
- **rbio** — OpenSSL reads raw socket bytes directly from a 64 KB receive
buffer that was filled by a single `fio_sock_read` call.
- **wbio** — OpenSSL appends encrypted output directly into a ~66 KB
encrypted-output buffer (4 × max TLS record).
This eliminates all intermediate copies while retaining full control over
buffering and partial writes.
Handshake responses (ServerHello, etc.) are flushed to the socket from inside
the `read` callback — the IO layer's `on_ready` loop doesn't run until the
handshake completes, so the backend must send them itself.
---
## Lifecycle and Error Behavior
```
fio_io_listen / fio_io_connect
│
└─► build_context (SSL_CTX built once per listener/connector)
│
└─► per-connection start (SSL_new + BIO setup + SSL_accept/SSL_connect)
│
├─ read (advances handshake until SSL_is_init_finished,
│ then SSL_read_ex)
├─ write (SSL_write_ex, encrypt into wbio buffer, flush socket)
├─ flush (send remaining enc_buf bytes)
│
├─ finish (SSL_shutdown + best-effort send of close_notify)
└─ cleanup (SSL_free — also frees both BIOs)
```
**Error codes** surfaced to the IO layer:
- `return 0` — peer closed cleanly (`SSL_ERROR_ZERO_RETURN`) or fatal SSL error.
- `return -1` with `errno = EWOULDBLOCK` — not enough data yet (normal event-loop
signalling; the reactor will retry on next readable event).
All non-fatal internal issues are logged via `FIO_LOG_ERROR` /
`FIO_LOG_WARNING` / `FIO_LOG_SECURITY`. Debug-level detail is at
`FIO_LOG_DDEBUG2`.
---
## Minimal Server Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf));
if (n)
fio_io_write(io, buf, n); /* echo */
}
static fio_io_protocol_s ECHO_PROTO = {
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
/* No certificate configured → self-signed ECDSA P-256 generated automatically */
fio_io_tls_s *tls = fio_io_tls_new();
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &ECHO_PROTO,
.tls = tls);
fio_io_tls_free(tls);
FIO_LOG_INFO("TLS echo server on :8443 (test: openssl s_client -connect localhost:8443)");
fio_io_start(0);
}
```
For a production server, load real certificates:
```c
fio_io_tls_cert_add(tls, "example.com", "cert.pem", "key.pem", NULL);
```
---
## Disambiguation
| Document | Scope |
|----------|-------|
| **This document** | OpenSSL 3.x IO-layer backend — plugs TLS into the reactor |
| [./405 tls13.md](./405 tls13.md) *(planned)* | Native TLS 1.3 IO backend (same `fio_io_functions_s` interface, no OpenSSL dependency) |
| [./190 tls13.md](./190 tls13.md) | Standalone TLS 1.3 crypto library (key schedule, record layer, raw machinery) |
| [./400 io-overview.md](./400 io-overview.md) | IO + TLS stack overview |
# Native TLS 1.3 IO Backend
```c
#define FIO_IO
#include "fio-stl/include.h"
```
IO-layer TLS 1.3 integration — for standalone crypto library see [`190 tls13.md`](./190 tls13.md).
This module wires the native TLS 1.3 state machines into the facil.io IO reactor
as a drop-in transport backend. When OpenSSL is unavailable at build time it
registers itself automatically; otherwise it is available on demand via
`fio_tls13_io_functions()`. No external dependencies required.
For the IO/TLS stack overview see [./400 io-overview.md](./400 io-overview.md).
For the OpenSSL backend (preferred when available) see [./405 openssl.md](./405 openssl.md).
---
## Activation
The module compiles when all of the following are true:
| Condition | Notes |
|-----------|-------|
| `FIO_IO` is defined | pulls in the IO reactor |
| `H___FIO_TLS13___H` guard is satisfied | `190 tls13.h` was included first |
| `FIO_NO_TLS` is **not** defined | opt-out guard checked by `include.h` before this backend is included |
A module-level constructor (`FIO_CONSTRUCTOR`) runs automatically before
`main()` **only when OpenSSL is absent** (`HAVE_OPENSSL` not defined and
`405 openssl.h` not included). It calls:
```c
fio_io_tls_default_functions(&FIO___TLS13_IO_FUNCS);
```
This makes every subsequent `fio_io_listen` / `fio_io_connect` call with a
`tls` argument use the native TLS 1.3 engine without any extra configuration.
When OpenSSL *is* present, the native backend compiles but does **not**
auto-register. Use `fio_tls13_io_functions()` to switch explicitly if needed.
---
## Public API
### `fio_tls13_io_functions`
```c
fio_io_functions_s fio_tls13_io_functions(void);
```
Returns the `fio_io_functions_s` vtable that wires the native TLS 1.3 engine
into the IO reactor:
| Field | Role |
|-------|------|
| `build_context` | Converts `fio_io_tls_s` into a per-listener/connector context |
| `free_context` | Deferred free of the context (via `fio_io_defer`) |
| `start` | Per-connection: allocate state, run ClientHello or await ServerHello |
| `read` | Non-blocking decrypt: advances handshake if needed, then decrypts records |
| `write` | Non-blocking encrypt: batches up to 4 TLS records (64 KB) per syscall |
| `flush` | Drains any pending handshake or encrypted bytes from internal buffers |
| `finish` | Sends a TLS `close_notify` alert before the TCP close |
| `cleanup` | Frees per-connection TLS state |
Normally you never call this directly — the constructor handles registration.
Use it to override the global default or set a per-protocol backend:
```c
/* Override: force native TLS 1.3 even when OpenSSL is present */
fio_io_functions_s tls13_funcs = fio_tls13_io_functions();
fio_io_tls_default_functions(&tls13_funcs);
```
Or set it on a specific protocol without changing the global default:
```c
fio_io_functions_s tls13_funcs = fio_tls13_io_functions();
MY_PROTOCOL.io_functions = tls13_funcs; /* per-protocol override */
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &MY_PROTOCOL,
.tls = tls);
```
---
## Configuring TLS — `fio_io_tls_s`
TLS parameters are carried in a `fio_io_tls_s` object (defined in
`401 io api.h`). Build one before calling `fio_io_listen` or `fio_io_connect`:
```c
/* Allocate (reference counted) */
fio_io_tls_s *tls = fio_io_tls_new();
/* Certificate — PEM files */
fio_io_tls_cert_add(tls,
"www.example.com", /* server_name (SNI) */
"cert.pem", /* public certificate or chain */
"key.pem", /* private key */
NULL); /* PEM password, or NULL */
/* ALPN protocol negotiation */
fio_io_tls_alpn_add(tls, "h2", on_http2_selected);
fio_io_tls_alpn_add(tls, "http/1.1", on_http1_selected);
/* Peer certificate verification */
fio_io_tls_trust_add(tls, NULL); /* use system trust store */
fio_io_tls_trust_add(tls, "ca.pem"); /* or a specific CA bundle */
/* Listen */
fio_io_listen(.url = "0.0.0.0:443",
.protocol = &MY_PROTOCOL,
.tls = tls);
fio_io_tls_free(tls); /* release your reference; the listener holds its own */
fio_io_start(0);
```
`fio_io_tls_s` is reference-counted. The backend duplicates the reference
during `build_context`; freeing yours after `fio_io_listen` is always safe.
---
## Certificates
### Loading from PEM files
When both `public_cert_file` and `private_key_file` are provided to
`fio_io_tls_cert_add`, the backend reads and parses the PEM files, loading
every `CERTIFICATE` block in the file as a DER chain entry. Three private key
types are supported:
| Key type | Constant | Notes |
|----------|----------|-------|
| ECDSA P-256 | `FIO_TLS13_SIGNATURE_ECDSA_SECP256R1_SHA256` | Recommended; small and fast |
| Ed25519 | `FIO_TLS13_SIGNATURE_ED25519` | Fastest signatures |
| RSA (any size) | `FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA256` | Requires `H___FIO_RSA___H` |
If PEM parsing fails, the backend logs `FIO_LOG_WARNING` and falls back to a
self-signed certificate. If the fallback also fails the context build fails
and `build_context` returns `NULL`.
### Self-signed fallback
When a **server** has no certificates configured, the backend generates a
self-signed P-256 ECDSA certificate on the fly using `fio_x509_self_signed_cert`
(requires `H___FIO_X509___H`):
| Property | Value |
|----------|-------|
| Key algorithm | ECDSA P-256 (128-bit security ≈ RSA-3072) |
| Signature | SHA-256 |
| SAN | set to `server_name` (defaults to `"localhost"`) |
Self-signed certificates are fine for development. Browsers will warn. Use a
CA-issued certificate (e.g. Let's Encrypt) in production.
If `H___FIO_X509___H` is not available when no PEM files are configured, the
context build fails with `FIO_LOG_ERROR`.
---
## ALPN
Register protocols with `fio_io_tls_alpn_add` before listen/connect. The
first registered protocol is the preferred default.
The backend collects all registered names into a comma-separated string (up
to 255 bytes per name; 255 characters total plus the NUL terminator). On handshake the server matches the
client's offered list against the registered names in registration order and
calls the corresponding `on_selected` callback on the `fio_io_s *` when
negotiation succeeds.
Protocol names must be 1–255 bytes. The internal list overflows when the
comma-separated list would exceed 255 characters — excess protocols are dropped with `FIO_LOG_ERROR`.
---
## Trust and Peer Verification
Trust configured with `fio_io_tls_trust_add` always refers to **peer**
verification: on **client** connections the backend verifies the server
certificate, on **server** connections it requests, requires, and verifies
the client certificate (mutual TLS). An empty trust list means **no peer
verification** — matching the OpenSSL backend.
| Scenario | Behaviour |
|----------|-----------|
| `fio_io_tls_trust_add(tls, NULL)` | system CA bundle used (loaded once, global) |
| `fio_io_tls_trust_add(tls, "ca.pem")` | user-supplied CA bundle (per-context) |
| No `fio_io_tls_trust_add` call, client mode | no server verification + `FIO_LOG_SECURITY` warning |
| No `fio_io_tls_trust_add` call, server mode | no client certificate requested |
| Trust configured, server mode | client certificate required and verified |
For mTLS, client certificate chains are verified against the trust store
(`fio_x509_verify_chain`) and the client's `CertificateVerify` signature is
validated against the leaf certificate's public key (Ed25519, ECDSA P-256 /
P-384, and RSA-PSS / PKCS#1 SHA-256 / SHA-384 schemes). A client that fails
verification is rejected with a TLS alert during the handshake.
After the handshake, inspect the peer's chain with
`fio_io_peer_info_next(io, &info)` (see the IO API docs) — each call parses
the next certificate in place (zero-copy) into a `fio_x509_cert_s`, exposing
the subject CN/DN, issuer, SAN entries, public key, validity, and a SHA-256
fingerprint for pinning, plus a `verified` flag for authorization decisions.
The system CA bundle is loaded **once** into a process-wide singleton
(`fio___tls13_sys_trust`) and shared read-only across all connections —
loading ~128 certs per outgoing connection is avoided. It is freed at process
exit via a `FIO_CALL_AT_EXIT` callback.
Platform CA bundle lookup order (POSIX):
```
/etc/ssl/cert.pem (macOS, FreeBSD)
/etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu)
/etc/pki/tls/certs/ca-bundle.crt (RHEL/CentOS)
/etc/ssl/ca-bundle.pem (openSUSE)
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (RHEL newer)
/usr/local/etc/ssl/cert.pem (FreeBSD ports)
```
On **Windows** the `ROOT` system certificate store is enumerated via the
CryptoAPI (`CertOpenSystemStoreA` / `CertEnumCertificatesInStore`). On MSVC,
`Crypt32.lib` is linked automatically via `#pragma comment`; other Windows
toolchains may need an explicit `Crypt32` link flag.
---
## Lifecycle and Error Behavior
```
fio_io_listen / fio_io_connect
│
└─► build_context (fio_io_tls_s → fio___tls13_context_s, once per listener)
│
└─► per-connection start
│
├─ client: ClientHello sent immediately
├─ server: awaits ClientHello
│
├─ read (advances handshake records; decrypts app data after)
├─ write (encrypts; up to 4 records batched per syscall)
├─ flush (drains handshake + enc_buf to socket)
│
├─ finish (sends encrypted close_notify alert, best-effort)
└─ cleanup (frees fio___tls13_connection_s)
```
**Internal buffers per connection** (flexible array member, single allocation):
| Region | Purpose | Size |
|--------|---------|------|
| `recv_buf` | Incoming encrypted data | `FIO_IO_BUFFER_PER_WRITE` (~64 KB) |
| `app_buf` | Decrypted plaintext ready to deliver | `FIO_IO_BUFFER_PER_WRITE` |
| `send_buf` | Outgoing handshake bytes | `FIO_IO_BUFFER_PER_WRITE` |
| `enc_buf` | Pre-allocated encryption output (4 max records) | ~66 KB |
**Error codes surfaced to the IO layer:**
- `return 0` from `read` — peer closed cleanly (EOF).
- `return -1` with `errno = EWOULDBLOCK` — no data yet; reactor retries on next readable event.
- `return -1` with `errno = ECONNRESET` — TLS handshake or decryption error; connection will be closed.
All internal errors are logged: `FIO_LOG_ERROR` for hard failures,
`FIO_LOG_WARNING` for soft failures (e.g. PEM fallback), `FIO_LOG_DEBUG2` /
`FIO_LOG_DDEBUG2` for per-connection detail.
**KeyUpdate** (RFC 8446 §4.6.3): when the peer requests a key update, a
`KeyUpdate` response is prepended to the next `write` syscall alongside
application data so they go out in a single call.
---
## Minimal Server Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf));
if (n)
fio_io_write(io, buf, n); /* echo */
}
static fio_io_protocol_s ECHO_PROTO = {
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
/* No certificate → self-signed P-256 generated automatically */
fio_io_tls_s *tls = fio_io_tls_new();
fio_io_listen(.url = "0.0.0.0:8443",
.protocol = &ECHO_PROTO,
.tls = tls);
fio_io_tls_free(tls);
FIO_LOG_INFO("TLS echo server on :8443");
fio_io_start(0);
}
```
For a production server, load real certificates:
```c
fio_io_tls_cert_add(tls, "example.com", "cert.pem", "key.pem", NULL);
```
---
## TLS Client Example
```c
#define FIO_LOG
#define FIO_IO
#include "fio-stl/include.h"
static void on_attach(fio_io_s *io) {
/* Handshake happens transparently through the TLS transport hooks. */
const char req[] = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
fio_io_write(io, req, sizeof(req) - 1);
}
static void on_data(fio_io_s *io) {
char buf[4096];
size_t n = fio_io_read(io, buf, sizeof(buf) - 1);
if (n) {
buf[n] = '\0';
FIO_LOG_INFO("response:\n%s", buf);
fio_io_close(io);
}
}
static fio_io_protocol_s CLIENT_PROTO = {
.on_attach = on_attach,
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
fio_io_tls_s *tls = fio_io_tls_new();
/* SNI hostname — system CA store is used automatically for verification */
fio_io_tls_cert_add(tls, "example.com", NULL, NULL, NULL);
fio_io_connect(.url = "example.com:443",
.protocol = &CLIENT_PROTO,
.tls = tls);
fio_io_tls_free(tls);
fio_io_start(0);
}
```
---
## Comparison with OpenSSL Backend
| Feature | Native TLS 1.3 | OpenSSL 3.x |
|---------|---------------|-------------|
| External dependency | None | OpenSSL 3.x |
| TLS versions | 1.3 only | 1.0–1.3 |
| Certificate key types | P-256, Ed25519, RSA | All |
| ALPN | Yes | Yes |
| Self-signed auto-cert | Yes (P-256) | Yes (P-256) |
| System trust store | Yes (multi-platform) | Yes |
| Session resumption (0-RTT) | No | Yes |
| OCSP stapling | No | Yes |
| Binary size impact | Smaller | Larger |
| Auto-registered when | OpenSSL absent | OpenSSL present |
The native backend is suitable for environments where TLS 1.3-only is
acceptable and minimising dependencies matters. If you need legacy TLS
versions, session resumption, OCSP stapling, or full certificate type
coverage, use the OpenSSL backend.
---
## Disambiguation
| Document | Scope |
|----------|-------|
| **This document** | Native TLS 1.3 IO backend — plugs TLS into the reactor via `fio_io_functions_s` |
| [./405 openssl.md](./405 openssl.md) | OpenSSL 3.x IO backend (same interface, preferred when available) |
| [./190 tls13.md](./190 tls13.md) | Standalone TLS 1.3 crypto library — key schedule, record layer, handshake state machines |
| [./400 io-overview.md](./400 io-overview.md) | IO + TLS stack overview |
# Pub/Sub — Publish/Subscribe Messaging (420 pubsub.h)
```c
#define FIO_PUBSUB
#include FIO_INCLUDE_FILE
```
> **Requires:** `FIO_IPC`. When using `include.h` the dependency is resolved automatically. When using the combined `fio-stl.h`, define `FIO_IPC` before `FIO_PUBSUB`.
Pub/Sub delivers messages to named channels across all worker processes and,
through pluggable engines, across machines or external brokers. It is the
broadcast layer that sits directly on top of IPC.
See [./400 io-overview.md](./400 io-overview.md) for where Pub/Sub fits in the
full IO stack, and [./404 ipc.md](./404 ipc.md) for the IPC transport it relies on.
---
## How it fits together
```
publisher (any thread/process)
│
│ fio_pubsub_publish(...)
▼
engine->publish() ← default: fio_pubsub_engine_ipc()
│
│ IPC message (fio_ipc_local / fio_ipc_broadcast)
▼
every process receives it
│
├─ matches channel → subscriber callbacks scheduled on queue
├─ matches patterns → pattern subscriber callbacks scheduled
└─ history manager → message stored for replay
```
Workers do not talk to each other directly. Every publish goes through the
master (via IPC), which fans the message out to all processes. Subscriptions
follow the same path in reverse: when a new channel is first subscribed in a
worker, the worker notifies the master so the master can track routing.
---
## Configuration Macros
Define these **before** including the header to override the defaults.
| Macro | Default | Meaning |
|-------|---------|---------|
| `FIO_PUBSUB_FUTURE_LIMIT_MS` | `60000` | Messages timestamped more than this many ms in the future are not delivered to subscribers immediately; they are handed to history managers instead. |
| `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT` | `1 << 28` (256 MiB) | Default byte ceiling for the built-in in-memory history cache. |
---
## Types
### `fio_pubsub_msg_s` — the message
```c
typedef struct fio_pubsub_msg_s {
fio_io_s *io; /* IO connection owning the subscription (may be NULL) */
void *udata; /* opaque value set on subscribe */
uint64_t timestamp; /* ms since epoch */
uint64_t id; /* unique message ID */
fio_buf_info_s channel; /* channel name — shared, do NOT mutate */
fio_buf_info_s message; /* message payload — shared, do NOT mutate */
int16_t filter; /* numerical namespace; negative values are reserved */
} fio_pubsub_msg_s;
```
Delivered to `on_message` callbacks. The `channel` and `message` buffers are
shared references into the underlying IPC buffer — do not store pointers to
them beyond the callback unless you call `fio_pubsub_msg2ipc` / `fio_ipc_dup`
first.
### `fio_pubsub_subscribe_args_s` — subscribe / unsubscribe arguments
```c
typedef struct {
fio_io_s *io; /* subscription owner (NULL = global) */
fio_buf_info_s channel; /* channel name or pattern */
void (*on_message)(fio_pubsub_msg_s *msg); /* required callback */
void (*on_unsubscribe)(void *udata); /* optional cleanup callback */
void *udata; /* opaque user data for callbacks */
fio_queue_s *queue; /* callback queue; NULL = IO queue */
uintptr_t *subscription_handle_ptr; /* out: handle for manual management */
uint64_t replay_since; /* replay history since this ms timestamp */
int16_t filter; /* numerical namespace (must match publish) */
uint8_t is_pattern; /* non-zero = channel is a glob pattern */
uint8_t master_only; /* non-zero = subscription lives in master */
} fio_pubsub_subscribe_args_s;
```
Both `fio_pubsub_subscribe` and `fio_pubsub_unsubscribe` use this struct. The
macro wrappers let you pass named arguments directly.
**Ownership rules:**
- When `io` is set, the subscription is stored in the IO object's environment
and cancelled automatically when the connection closes. Only one subscription
per `(channel, filter, is_pattern)` triple is allowed per IO.
- When `io` is NULL, the subscription is global — one per
`(channel, filter, is_pattern)` triple for the whole process.
- When `subscription_handle_ptr` is set, the `io` field is ignored and the
handle is written to the pointer. The caller is responsible for calling
`fio_pubsub_unsubscribe(.subscription_handle_ptr = &handle)` explicitly.
Forgetting to do so leaks the subscription.
**If `on_message` is NULL and `io` is set**, the subscription calls
`io->protocol->on_pubsub(msg)` automatically (protocol-level dispatch).
### `fio_pubsub_publish_args_s` — publish arguments
```c
typedef struct {
fio_pubsub_engine_s const *engine; /* NULL = default engine */
fio_io_s *from; /* exclude this IO from receiving the message */
uint64_t id; /* 0 = auto-generate random ID */
uint64_t timestamp; /* 0 = current reactor tick (ms) */
fio_buf_info_s channel; /* target channel name */
fio_buf_info_s message; /* message payload */
int16_t filter; /* numerical namespace */
} fio_pubsub_publish_args_s;
```
---
## Subscribe / Unsubscribe
### `fio_pubsub_subscribe`
```c
void fio_pubsub_subscribe(fio_pubsub_subscribe_args_s args);
/* macro wrapper — enables named arguments: */
#define fio_pubsub_subscribe(...) \
fio_pubsub_subscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
Subscribe to a channel. The actual subscription is queued as a deferred task
on the IO thread, so it is safe to call from any thread.
```c
/* Global subscription */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("events"),
.on_message = handle_event);
/* Per-connection subscription — auto-cancelled on close */
fio_pubsub_subscribe(.io = client_io,
.channel = FIO_BUF_INFO1("user:123"),
.on_message = handle_user_msg,
.udata = user_ctx);
/* Pattern subscription (glob) */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("chat:*"),
.on_message = handle_chat,
.is_pattern = 1);
/* Replay history from the last 5 minutes */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = handle_news,
.replay_since = fio_io_last_tick() - 5*60*1000);
/* Manual handle for explicit control */
uintptr_t sub_handle = 0;
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("ticker"),
.on_message = handle_tick,
.subscription_handle_ptr = &sub_handle);
/* … later … */
fio_pubsub_unsubscribe(.subscription_handle_ptr = &sub_handle);
```
> **Note:** pattern subscriptions do not support history replay (`replay_since`
> is silently ignored for patterns).
> **Note:** `master_only = 1` creates the subscription only in the master
> process and is automatically removed from forked workers after `fork()`.
### `fio_pubsub_unsubscribe`
```c
int fio_pubsub_unsubscribe(fio_pubsub_subscribe_args_s args);
/* macro wrapper: */
#define fio_pubsub_unsubscribe(...) \
fio_pubsub_unsubscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
Remove a subscription. Returns `0` on success, `-1` if not found.
Only `io`, `channel`, `filter`, `is_pattern`, and `subscription_handle_ptr`
are used for matching — `on_message` and `udata` are ignored.
```c
fio_pubsub_unsubscribe(.io = client_io,
.channel = FIO_BUF_INFO1("user:123"));
fio_pubsub_unsubscribe(.channel = FIO_BUF_INFO1("chat:*"),
.is_pattern = 1);
```
---
## Publish
### `fio_pubsub_publish`
```c
void fio_pubsub_publish(fio_pubsub_publish_args_s args);
/* macro wrapper: */
#define fio_pubsub_publish(...) \
fio_pubsub_publish((fio_pubsub_publish_args_s){__VA_ARGS__})
```
Publish a message. Thread-safe, callable from any process or thread. In
workers the default engine routes the message through IPC to the master, which
fans it out to all processes.
```c
/* Simple publish */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("events"),
.message = FIO_BUF_INFO1("something happened"));
/* With namespace filter */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("updates"),
.message = FIO_BUF_INFO2(data, data_len),
.filter = 42);
/* Exclude the sender (echo suppression) */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat:room1"),
.message = FIO_BUF_INFO1("hello"),
.from = sender_io);
/* Cluster-wide (all machines) */
fio_pubsub_publish(.engine = fio_pubsub_engine_cluster(),
.channel = FIO_BUF_INFO1("global"),
.message = FIO_BUF_INFO1("cluster alert"));
```
### `fio_pubsub_defer`
```c
void fio_pubsub_defer(fio_pubsub_msg_s *msg);
```
Call from inside `on_message` to push the *same* callback invocation to the
end of the queue. The message and subscription references are managed
automatically.
```c
void on_message(fio_pubsub_msg_s *msg) {
if (not_ready_yet()) {
fio_pubsub_defer(msg); /* try again later */
return;
}
/* process msg… */
}
```
---
## Namespace Filters
The `filter` field (`int16_t`) is a numerical namespace. Publishers and
subscribers must use the same `filter` value to match. Negative values are
reserved for internal use.
Filters are useful when two unrelated subsystems share channel names. Using
`filter = 1` for one and `filter = 2` for the other keeps messages isolated
without any naming conventions.
```c
/* System 1 uses filter 1 */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("status"), .filter = 1, …);
fio_pubsub_publish (.channel = FIO_BUF_INFO1("status"), .filter = 1, …);
/* System 2 uses filter 2 — completely separate, same channel name */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("status"), .filter = 2, …);
fio_pubsub_publish (.channel = FIO_BUF_INFO1("status"), .filter = 2, …);
```
---
## IO Subscription Helper
### `FIO_ON_MESSAGE_SEND_MESSAGE`
```c
void FIO_ON_MESSAGE_SEND_MESSAGE(fio_pubsub_msg_s *msg);
```
A ready-made `on_message` callback that writes `msg->message` directly to
`msg->io`. Zero-copy: it duplicates the underlying IPC buffer reference and
hands it to the IO write queue.
```c
/* Wire clients to a channel with one line */
fio_pubsub_subscribe(.io = client_io,
.channel = FIO_BUF_INFO1("feed"),
.on_message = FIO_ON_MESSAGE_SEND_MESSAGE);
```
It can also serve as the protocol-level `on_pubsub` callback:
```c
fio_io_protocol_s MY_PROTOCOL = {
.on_attach = my_on_attach,
.on_data = my_on_data,
.on_pubsub = FIO_ON_MESSAGE_SEND_MESSAGE,
};
```
---
## Pattern Matching
### `fio_pubsub_match_fn_set`
```c
void fio_pubsub_match_fn_set(uint8_t (*match_cb)(fio_str_info_s pattern,
fio_str_info_s name));
```
Override the function used to test pattern subscriptions. Pass `NULL` to
restore the default (`fio_glob_match`), which supports `*`, `?`, and `[sets]`.
```c
uint8_t my_regex_match(fio_str_info_s pattern, fio_str_info_s name) {
/* … custom logic … */
return matched ? 1 : 0;
}
fio_pubsub_match_fn_set(my_regex_match);
fio_pubsub_match_fn_set(NULL); /* restore glob */
```
---
## Engines
Engines decide *how* messages are distributed. The default engine sends
messages to the local process group (master + all workers on the current
machine). Swap the engine to reach external brokers or remote machines.
### Built-in engines
| Function | Scope |
|----------|-------|
| `fio_pubsub_engine_ipc()` | master + all workers on **this machine** only |
| `fio_pubsub_engine_cluster()` | master + workers on **all machines** in the cluster |
```c
fio_pubsub_engine_s const *fio_pubsub_engine_ipc(void);
fio_pubsub_engine_s const *fio_pubsub_engine_cluster(void);
```
The default engine starts as `fio_pubsub_engine_ipc()` and is automatically
upgraded to `fio_pubsub_engine_cluster()` at `PRE_START` time when
`fio_ipc_cluster_port()` returns a non-zero value (i.e., when cluster IPC is
configured before `fio_io_start`).
### Default engine accessors
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default(void);
fio_pubsub_engine_s const *fio_pubsub_engine_default_set(
fio_pubsub_engine_s const *engine);
```
`fio_pubsub_engine_default_set(NULL)` restores the auto-selected default
(cluster engine if cluster port is set, IPC engine otherwise).
### Custom engine interface
```c
typedef struct fio_pubsub_engine_s {
void (*detached) (const struct fio_pubsub_engine_s *eng);
void (*subscribe) (const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*psubscribe) (const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*unsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*punsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel, int16_t filter);
void (*publish) (const struct fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg);
} fio_pubsub_engine_s;
```
**Execution context:**
- `publish` — called from any thread or process
- all other callbacks — called from the **master process**, **IO thread** only
Callbacks must not block. Defer slow operations with `fio_io_defer`.
Missing callbacks are filled with safe defaults on `fio_pubsub_engine_attach`: subscription callbacks become no-ops; a NULL `publish` falls back to local IPC delivery.
### `fio_pubsub_engine_attach` / `fio_pubsub_engine_detach`
```c
void fio_pubsub_engine_attach(fio_pubsub_engine_s *engine);
void fio_pubsub_engine_detach(fio_pubsub_engine_s *engine);
```
`attach` registers the engine and immediately notifies it of all existing
channel and pattern subscriptions (via `subscribe` / `psubscribe`).
`detach` removes the engine and calls its `detached` callback. If the detached
engine was the current default, the default is automatically reset.
```c
/* Example: a custom engine that bridges to NATS */
static fio_pubsub_engine_s nats_engine = {
.subscribe = nats_subscribe,
.psubscribe = nats_subscribe, /* reuse for patterns if needed */
.unsubscribe = nats_unsubscribe,
.punsubscribe= nats_unsubscribe,
.publish = nats_publish,
.detached = nats_cleanup,
};
/* attach before or after fio_io_start — both work */
fio_pubsub_engine_attach(&nats_engine);
fio_pubsub_engine_default_set(&nats_engine);
```
See [./422 redis.md](./422 redis.md) *(planned)* for the built-in Redis engine,
which is a production example of this interface.
---
## History / Replay
The history system caches messages for late-joining subscribers. History lives
in the **master process only** and is separate from the engine interface.
When `replay_since` is set on a subscription, the subscriber receives all
cached messages with `timestamp >= replay_since` before live messages begin.
### Built-in in-memory cache
```c
fio_pubsub_history_s const *fio_pubsub_history_cache(size_t size_limit);
```
Returns the built-in cache manager and sets its byte ceiling. The built-in
cache is initialized by default but **not attached automatically**; call
`fio_pubsub_history_attach(fio_pubsub_history_cache(size), priority)` to begin
storing messages for replay.
`size_limit = 0` inherits from environment variables in order:
1. `WEBSITE_MEMORY_LIMIT_MB` (× 1 MiB)
2. `WEBSITE_MEMORY_LIMIT_KB` (× 1 KiB)
3. `WEBSITE_MEMORY_LIMIT` (bytes)
4. `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT` (256 MiB)
When the cache overflows its limit, the oldest messages across all channels
are evicted first.
```c
/* Attach the built-in cache with a 64 MiB ceiling */
fio_pubsub_history_attach(fio_pubsub_history_cache(64 * 1024 * 1024), 100);
```
### Attach / detach history managers
```c
int fio_pubsub_history_attach(const fio_pubsub_history_s *manager,
uint8_t priority);
void fio_pubsub_history_detach(const fio_pubsub_history_s *manager);
```
Multiple managers can be active simultaneously. All receive `push()` on every
published message. For replay, managers are consulted in **descending priority
order** via `oldest()` to find a manager with enough history coverage; `replay()`
is called on the selected manager once.
### Custom history manager interface
```c
typedef struct fio_pubsub_history_s {
void (*detached)(const struct fio_pubsub_history_s *hist);
int (*push) (const struct fio_pubsub_history_s *hist,
fio_pubsub_msg_s *msg);
int (*replay) (const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter,
uint64_t since,
void (*on_message)(fio_pubsub_msg_s *msg, void *udata),
void (*on_done)(void *udata),
void *udata);
uint64_t (*oldest) (const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter);
} fio_pubsub_history_s;
```
**Execution context:** all callbacks — **master process**, **IO thread** only.
Do not block; use `fio_io_defer` for slow operations.
`replay` **must** call `on_done(udata)` when finished (success or failure) —
the IPC reply mechanism depends on it.
`replay` returns `0` if it handled the request. Returning `-1` does **not**
trigger fallback; use `oldest()` to return `UINT64_MAX` when the manager cannot
cover that channel/filter.
`oldest` returns `UINT64_MAX` if no history is available for the channel.
Missing callbacks are replaced with stubs on `fio_pubsub_history_attach`.
### `fio_pubsub_history_push_all`
```c
void fio_pubsub_history_push_all(fio_pubsub_msg_s *msg);
```
Push a message to all attached history managers without delivering it locally.
Use this from a custom engine when the engine receives a message that should be
stored for replay but is not routed through the normal publish path.
---
## Advanced: IPC Buffer Access
### `fio_pubsub_msg2ipc` / `fio_pubsub_ipc2msg`
```c
fio_ipc_s *fio_pubsub_msg2ipc(fio_pubsub_msg_s *msg);
fio_pubsub_msg_s fio_pubsub_ipc2msg(fio_ipc_s *ipc);
```
`fio_pubsub_msg2ipc` returns (and detaches) the underlying `fio_ipc_s` for
the message. Use with `fio_ipc_dup` to keep the buffer alive beyond the
callback, enabling true zero-copy deferral.
`fio_pubsub_ipc2msg` is the inverse: reconstruct a `fio_pubsub_msg_s` from a
raw IPC buffer.
> **Fragile.** These functions bypass the normal lifecycle. Incorrect use will
> cause use-after-free bugs. Prefer `fio_pubsub_defer` for simple deferral.
---
## Execution Context Summary
| Operation | Where it runs |
|-----------|---------------|
| `fio_pubsub_publish` | any thread, any process |
| `fio_pubsub_subscribe` / `fio_pubsub_unsubscribe` | any thread, any process |
| `on_message` callback | IO thread, any process (per `queue`) |
| `on_unsubscribe` callback | IO thread, any process |
| Engine `subscribe` / `unsubscribe` / `psubscribe` / `punsubscribe` | **master**, IO thread |
| Engine `publish` | any thread, any process |
| History `push` / `replay` / `oldest` / `detached` | **master**, IO thread |
Callbacks must **not block** — offload slow work with `fio_io_defer` or
route to a background `fio_io_async_s` pool.
---
## Examples
### Chat server — per-connection subscription
```c
#define FIO_PUBSUB
#include FIO_INCLUDE_FILE
static void on_data(fio_io_s *io) {
char buf[256];
size_t len = fio_io_read(io, buf, sizeof(buf) - 1);
if (!len) return;
/* Publish whatever the client sends to the "chat" channel */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat"),
.message = FIO_BUF_INFO2(buf, len),
.from = io); /* suppress echo */
}
static void on_attach(fio_io_s *io) {
/* Every new client gets live chat messages forwarded to it */
fio_pubsub_subscribe(.io = io,
.channel = FIO_BUF_INFO1("chat"),
.on_message = FIO_ON_MESSAGE_SEND_MESSAGE);
}
static fio_io_protocol_s CHAT_PROTOCOL = {
.on_attach = on_attach,
.on_data = on_data,
.on_timeout = fio_io_touch,
};
int main(void) {
fio_io_listen(.url = "0.0.0.0:3000", .protocol = &CHAT_PROTOCOL);
fio_io_start(4); /* 4 worker processes */
return 0;
}
```
### Pattern subscription
```c
static void on_any_chat(fio_pubsub_msg_s *msg) {
printf("[%.*s]: %.*s\n",
(int)msg->channel.len, msg->channel.buf,
(int)msg->message.len, msg->message.buf);
}
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("chat:*"),
.on_message = on_any_chat,
.is_pattern = 1);
/* Matches "chat:room1", "chat:lobby", "chat:anything" */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("chat:room1"),
.message = FIO_BUF_INFO1("hello room"));
```
### History replay (late subscriber)
```c
static void on_news(fio_pubsub_msg_s *msg) {
printf("[%llu] %.*s\n",
(unsigned long long)msg->timestamp,
(int)msg->message.len, msg->message.buf);
}
/* Subscribe and receive all messages from the last 10 minutes */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("news"),
.on_message = on_news,
.replay_since = fio_io_last_tick() - 10*60*1000);
```
Attach the built-in cache, or add a custom persistence layer:
```c
/* Attach built-in cache with a 32 MiB ceiling */
fio_pubsub_history_attach(fio_pubsub_history_cache(32 * 1024 * 1024), 50);
/* Attach a custom database-backed manager at higher priority */
fio_pubsub_history_attach(&my_db_history_manager, 100);
```
### Cluster-wide publish
```c
int main(void) {
/* Enable cluster RPC on port 9999 — must be set before fio_io_start */
fio_ipc_cluster_listen(9999);
/* Default engine auto-upgrades to cluster engine at PRE_START */
fio_io_start(4);
return 0;
}
/* Anywhere, after startup — goes to every machine in the cluster */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("invalidate"),
.message = FIO_BUF_INFO1("cache_key_42"));
/* Or explicitly select the engine */
fio_pubsub_publish(.engine = fio_pubsub_engine_cluster(),
.channel = FIO_BUF_INFO1("invalidate"),
.message = FIO_BUF_INFO1("cache_key_42"));
```
### Custom engine (skeleton)
```c
static void my_engine_subscribe(const fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter) {
/* tell external broker to subscribe */
(void)eng; (void)filter;
external_broker_subscribe(channel.buf, channel.len);
}
static void my_engine_publish(const fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg) {
/* forward to external broker */
(void)eng;
external_broker_publish(msg->channel.buf, msg->channel.len,
msg->message.buf, msg->message.len);
}
static void my_engine_cleanup(const fio_pubsub_engine_s *eng) {
(void)eng;
external_broker_disconnect();
}
static fio_pubsub_engine_s my_engine = {
.subscribe = my_engine_subscribe,
.psubscribe = my_engine_subscribe, /* same logic for patterns, or differentiate */
.unsubscribe = NULL, /* NULL → filled with no-op on attach */
.punsubscribe = NULL,
.publish = my_engine_publish,
.detached = my_engine_cleanup,
};
/* Register before or after fio_io_start */
fio_pubsub_engine_attach(&my_engine);
fio_pubsub_engine_default_set(&my_engine);
```
---
## Related
- [./400 io-overview.md](./400 io-overview.md) — full IO/IPC/PubSub stack diagram
- [./404 ipc.md](./404 ipc.md) — IPC transport, `fio_ipc_cluster_listen`, `fio_ipc_s` lifetime
- [./422 redis.md](./422 redis.md) *(planned)* — Redis as a pub/sub engine and command client
# Redis — Pub/Sub Engine and Command Client (422 redis.h)
```c
#define FIO_REDIS
#include FIO_INCLUDE_FILE
```
> **Requires:** `FIO_IO`, `FIO_PUBSUB`, `FIO_FIOBJ`, `FIO_RESP3`. When using `include.h` all dependencies are resolved automatically.
The Redis module does two things: it acts as a **Pub/Sub engine** that connects facil.io's Pub/Sub system to a Redis server, enabling cross-machine message distribution; and it acts as a **Redis command client** for arbitrary commands (`GET`, `SET`, `INCR`, etc.).
See [./400 io-overview.md](./400 io-overview.md) for where Redis fits in the full IO stack, [./420 pubsub.md](./420 pubsub.md) for the Pub/Sub engine interface, [./404 ipc.md](./404 ipc.md) for the IPC transport workers use to reach the master, and [./250 fiobj.md](./250 fiobj.md) for how FIOBJ types map to RESP replies.
---
## Architecture
Only the **master** process opens a TCP connection to Redis. Workers never connect directly. Instead:
- Workers forward `fio_redis_send()` calls to the master via IPC; the master executes the command and replies back.
- Workers forward `fio_pubsub_publish()` calls to the master via IPC; the master sends `PUBLISH` to Redis.
- Incoming subscription messages arrive as RESP3 push frames on the master's connection and are fanned out to all workers via the normal Pub/Sub IPC infrastructure.
**RESP3 is required (Redis ≥ 6.0).** The engine negotiates the protocol with a `HELLO 3` handshake on every (re)connection — authentication folds into `HELLO`. There is **no RESP2 fallback**: if the handshake fails (old server, bad credentials), the engine logs a hard error and stops.
After `HELLO 3`, `SUBSCRIBE` no longer commandeers the connection, so the master maintains a **single TCP connection** per engine:
| Traffic | Handling |
|---|---|
| Commands (`fio_redis_send`), `PUBLISH`, `PING`, `HELLO` | Lock-step queue (`should_wait`) — the next command is sent only after the reply arrives |
| `SUBSCRIBE` / `PSUBSCRIBE` / `UNSUBSCRIBE` / `PUNSUBSCRIBE` | Same queue, no-wait — classified by verb at accept time, popped as soon as they are sent; confirmations arrive as RESP3 **push frames**, never command replies, so they cannot desynchronize the queue |
| Incoming pub/sub messages | RESP3 **push frames**, routed to the push handler and re-published locally via `fio_pubsub_engine_ipc()` (zero-copy from the read buffer) |
All outgoing bytes flow through the single command queue. `HELLO` is always the first command on the wire of every (re)connection (queued at the head in `on_attach`, before the resubscribe batches and any leftover commands), and nothing is written while the TCP connection is still in progress (`conn.ready` gate).
```
Master Process
┌──────────────────────────────────────────┐
│ connection ─────── Redis Server │
│ (commands / PUBLISH / PING / HELLO │
│ + SUBSCRIBE family / push frames) │
└──────────────────────────────────────────┘
▲ │
│ IPC │ pub/sub IPC fan-out
│ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Worker 1 │ │ Worker 2 │ │ Worker N │
└────────────┘ └────────────┘ └────────────┘
```
In single-process mode (`fio_io_start(0)`) the process is both master and worker; all operations go directly to Redis with no IPC overhead.
---
## Setup
```c
#define FIO_LOG
#define FIO_REDIS
#include FIO_INCLUDE_FILE
int main(void) {
/* Create the engine BEFORE fio_io_start() (before fork). */
fio_pubsub_engine_s *redis = fio_redis_new(
.url = "redis://localhost:6379",
.auth = "my_password" /* optional */
);
/* Dup before attach — attach transfers your reference to the system. */
fio_redis_dup(redis); /* ref: 1 → 2 */
fio_pubsub_engine_attach(redis); /* system takes the original ref */
fio_io_start(4); /* master connects; workers use IPC */
fio_pubsub_engine_detach(redis); /* system releases its ref: 2 → 1 */
fio_redis_free(redis); /* caller releases dup: 1 → 0 → destroy */
}
```
`fio_redis_new()` **must** be called before `fio_io_start()`. Creating an engine from a worker process is not supported.
---
## Configuration
### `FIO_REDIS_READ_BUFFER`
```c
#define FIO_REDIS_READ_BUFFER 65536 /* default: 64 KiB */
```
Read buffer size for the connection. The master allocates `FIO_REDIS_READ_BUFFER` bytes of buffer space inside each engine. Note that large replies do **not** need to fit the buffer: blob strings larger than `FIO_RESP3_STREAM_THRESHOLD` (see [./004 resp3.md](./004 resp3.md)) are streamed incrementally. The effective size cap is `payload_limit` (see below).
### `FIO_REDIS_MAX_BATCH`
```c
#define FIO_REDIS_MAX_BATCH 128 /* default: 128 messages */
```
Maximum number of complete messages processed per `on_data` event. When the cap is reached with more data buffered, processing continues in a deferred task, keeping any single event-loop callback small.
---
## Types
### `fio_redis_args_s`
```c
typedef struct {
const char *url; /* Redis server URL; NULL → "localhost:6379" */
const char *auth; /* Password for HELLO ... AUTH default ; NULL = no auth */
size_t auth_len; /* Length of auth; 0 = strlen(auth) */
uint8_t ping_interval;/* Keepalive interval in seconds; 0 → 30 s */
size_t payload_limit;/* Per-message budget in bytes; 0 → 16 MiB */
} fio_redis_args_s;
```
**`url`** accepted formats:
| Format | Example |
|---|---|
| Scheme with port | `"redis://host:6380"` |
| Scheme, default port | `"redis://host"` |
| Host and port | `"host:6380"` |
| Host only | `"myredis"` |
| NULL or `""` | → `localhost:6379` |
**`ping_interval`** — the IO reactor sends a `PING` on the connection if it has been idle this many seconds. Default: **30 seconds**. The protocol error timeout (detecting a hung connection) is also governed by this value.
**`payload_limit`** — cumulative payload budget **per top-level Redis message**, in bytes. Default: **16 MiB** (`16 << 20`). The budget is charged as:
```
total = Σ(all string payload bytes) + 32 × (count of ALL objects)
```
Every object in the reply (String, Array, Map, Number, Bool, etc.) costs 32 bytes of budget; string payload bytes are charged in full — for fixed-length blob strings, **before** the buffer is allocated. A breach logs an error and disconnects the engine. This protects against hostile or corrupt servers declaring huge `$` allocations or sending oversized replies. Pub/sub push frames share the same per-message budget.
---
## Reference Counting and Ownership
The engine uses reference counting (`FIO_REF`). Three things independently hold or transfer refs:
1. **Caller**: `fio_redis_new()` returns `ref = 1`. The caller owns this ref and must release it with `fio_redis_free()`.
2. **Pub/Sub system**: `fio_pubsub_engine_attach()` prepares the engine for Pub/Sub use. The Redis engine keeps its Pub/Sub reference on first `SUBSCRIBE` / `PSUBSCRIBE` and releases it from `on_detached`; call `fio_redis_dup()` before attach if you need to keep using the pointer after attaching.
3. **Internal deferred tasks**: The engine dups before scheduling an IO-deferred task and frees at the end. These are invisible to the caller.
**Usage patterns:**
```c
/* Pattern A — with Pub/Sub */
fio_pubsub_engine_s *redis = fio_redis_new(...); /* ref = 1 */
fio_redis_dup(redis); /* ref = 2 (keep a copy) */
fio_pubsub_engine_attach(redis); /* system takes original ref */
/* ...use pub/sub... */
fio_pubsub_engine_detach(redis); /* system releases → ref = 1 */
fio_redis_free(redis); /* caller releases → ref = 0 → destroy */
/* Pattern B — command client only, no pub/sub */
fio_pubsub_engine_s *redis = fio_redis_new(...); /* ref = 1 */
/* ...send commands... */
fio_redis_free(redis); /* ref = 0 → destroy */
```
You do **not** have to detach before freeing. If you free while the pub/sub system still holds its ref, the engine remains alive until detach (or shutdown) fires the `on_detached` callback.
---
## API
### `fio_redis_new`
```c
fio_pubsub_engine_s *fio_redis_new(fio_redis_args_s args);
#define fio_redis_new(...) fio_redis_new((fio_redis_args_s){__VA_ARGS__})
```
Creates a Redis engine with `ref = 1`. The macro overload enables named arguments.
Returns a pointer to `fio_pubsub_engine_s` on success, `NULL` on allocation failure.
The engine is typed as `fio_pubsub_engine_s *` so it can be passed directly to `fio_pubsub_engine_attach()`. Pass the same pointer to `fio_redis_dup()`, `fio_redis_free()`, and `fio_redis_send()`.
The connection is deferred to the IO reactor; the engine does nothing until `fio_io_start()` is called.
```c
fio_pubsub_engine_s *redis = fio_redis_new(
.url = "redis://10.0.0.5:6379",
.auth = "s3cr3t",
.ping_interval = 60
);
```
### `fio_redis_dup`
```c
fio_pubsub_engine_s *fio_redis_dup(fio_pubsub_engine_s *engine);
```
Atomically increments the reference count and returns the engine. Returns `NULL` if `engine` is `NULL`.
Each `fio_redis_dup()` must be balanced with a `fio_redis_free()`.
### `fio_redis_free`
```c
void fio_redis_free(fio_pubsub_engine_s *engine);
```
Releases the caller's reference. When the count reaches zero, destroys the engine immediately: sets `running = 0`, closes the connection, drains the command queue (invoking any pending callbacks with `FIOBJ_INVALID`), and frees memory.
Safe to call with `NULL` (no-op).
### `fio_redis_state`
```c
typedef enum {
FIO_REDIS_STATE_ERROR,
FIO_REDIS_STATE_CONNECTING,
FIO_REDIS_STATE_CONNECTED,
} fio_redis_state_e;
fio_redis_state_e fio_redis_state(const fio_pubsub_engine_s *engine);
```
Returns a Redis connection-state snapshot when called from the IO thread:
| State | Meaning |
|---|---|
| `FIO_REDIS_STATE_ERROR` | `engine` is `NULL`, or `HELLO 3` failed and disabled the engine. |
| `FIO_REDIS_STATE_CONNECTING` | No TCP socket is attached, or the socket is awaiting its `HELLO 3` reply. |
| `FIO_REDIS_STATE_CONNECTED` | The TCP socket is attached and its RESP3 `HELLO 3` handshake completed. |
This is an observability API; callers may send commands in every state. Commands queue until the handshake completes.
### `fio_redis_send`
```c
int fio_redis_send(fio_pubsub_engine_s *engine,
FIOBJ command,
void (*callback)(fio_pubsub_engine_s *e,
FIOBJ reply,
void *udata),
void *udata);
```
Sends a Redis command. `command` must be a `FIOBJ_T_ARRAY` whose elements are the command verb and arguments as `FIOBJ_T_STRING` (or numbers). `callback` is optional (pass `NULL` to fire-and-forget).
Returns `0` on success, `-1` if `engine` is `NULL` or `command` is not a FIOBJ array.
**On the master**: the command is serialized to RESP and queued on the connection. Commands are sent one at a time (lock-step); the next command is sent after the reply for the previous one arrives.
**On a worker**: the RESP bytes are forwarded to the master via IPC. The master executes the command, serializes the reply to RESP, and sends it back. The worker deserializes and calls the callback on its IO thread. This is transparent to the caller.
**Never** pass `SUBSCRIBE`, `PSUBSCRIBE`, `UNSUBSCRIBE`, or `PUNSUBSCRIBE` to `fio_redis_send()`. Subscriptions must go through `fio_pubsub_subscribe()` so the Pub/Sub system tracks them (local delivery, resubscription on reconnect). Direct sends bypass that bookkeeping and their confirmations (RESP3 push frames) are ignored.
**Callback signature:**
```c
void my_callback(fio_pubsub_engine_s *e, FIOBJ reply, void *udata);
```
`reply` is `FIOBJ_INVALID` if the engine was destroyed or the connection was lost before the reply arrived. Otherwise it is a FIOBJ object:
| Redis response | FIOBJ type |
|---|---|
| Bulk string / simple string | `FIOBJ_T_STRING` |
| Integer | `FIOBJ_T_NUMBER` |
| Array | `FIOBJ_T_ARRAY` |
| Map (RESP3) | `FIOBJ_T_HASH` |
| Null | `fiobj_null()` |
| Boolean (RESP3) | `fiobj_true()` / `fiobj_false()` |
| Double (RESP3) | `FIOBJ_T_FLOAT` |
| Bignum (RESP3) | `FIOBJ_T_STRING` |
| Error | `FIOBJ_T_STRING` (warning logged) |
The callback owns nothing — `reply` is freed by the engine after the callback returns. Copy any data you need to keep.
---
## Building Commands
Commands are FIOBJ arrays of strings (and optionally numbers). Build them, pass to `fio_redis_send()`, then free them — the engine serializes to RESP before returning.
```c
/* SET mykey "hello" */
FIOBJ cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("SET", 3));
fiobj_array_push(cmd, fiobj_str_new_cstr("mykey", 5));
fiobj_array_push(cmd, fiobj_str_new_cstr("hello", 5));
fio_redis_send(redis, cmd, NULL, NULL); /* fire-and-forget */
fiobj_free(cmd);
/* GET mykey */
static void on_get(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
fio_str_info_s s = fiobj2cstr(reply);
printf("value: %.*s\n", (int)s.len, s.buf);
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("GET", 3));
fiobj_array_push(cmd, fiobj_str_new_cstr("mykey", 5));
fio_redis_send(redis, cmd, on_get, NULL);
fiobj_free(cmd);
/* INCR counter */
static void on_incr(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
printf("counter now: %ld\n", (long)fiobj2i(reply));
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("INCR", 4));
fiobj_array_push(cmd, fiobj_str_new_cstr("counter", 7));
fio_redis_send(redis, cmd, on_incr, NULL);
fiobj_free(cmd);
```
Array replies (e.g., `LRANGE`, `HGETALL`):
```c
static void on_hgetall(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
size_t n = fiobj_array_count(reply);
for (size_t i = 0; i + 1 < n; i += 2) {
fio_str_info_s k = fiobj2cstr(fiobj_array_get(reply, (int32_t)i));
fio_str_info_s v = fiobj2cstr(fiobj_array_get(reply, (int32_t)(i + 1)));
printf(" %.*s = %.*s\n", (int)k.len, k.buf, (int)v.len, v.buf);
}
(void)e; (void)udata;
}
cmd = fiobj_array_new();
fiobj_array_push(cmd, fiobj_str_new_cstr("HGETALL", 7));
fiobj_array_push(cmd, fiobj_str_new_cstr("myhash", 6));
fio_redis_send(redis, cmd, on_hgetall, NULL);
fiobj_free(cmd);
```
See [./250 fiobj.md](./250 fiobj.md) for FIOBJ construction and inspection helpers.
---
## Pub/Sub Integration
When attached via `fio_pubsub_engine_attach()`, the Redis engine implements the `fio_pubsub_engine_s` interface:
| Pub/Sub event | Redis action |
|---|---|
| New channel subscription | `SUBSCRIBE channel` (fire-and-forget) |
| New pattern subscription | `PSUBSCRIBE pattern` (fire-and-forget) |
| Channel unsubscribe | `UNSUBSCRIBE channel` (fire-and-forget) |
| Pattern unsubscribe | `PUNSUBSCRIBE pattern` (fire-and-forget) |
| `fio_pubsub_publish(...)` | `PUBLISH channel message` through the command queue (workers route via IPC) |
| Incoming Redis push message | Re-published locally with `fio_pubsub_engine_ipc()` to fan out to all processes (zero-copy from the read buffer) |
The `filter` parameter from facil.io's Pub/Sub system is ignored by the Redis engine — Redis does not support numeric filter namespaces.
Example: subscribing to Redis channels through facil.io's Pub/Sub API:
```c
static fio_pubsub_engine_s *redis_engine; /* global, set before fork */
static void on_message(fio_pubsub_msg_s *msg) {
printf("channel: %.*s message: %.*s\n",
(int)msg->channel.len, msg->channel.buf,
(int)msg->message.len, msg->message.buf);
}
/* Called after IO reactor starts (FIO_CALL_ON_START) */
static void on_start(void *udata) {
/* Regular subscription — Redis engine sends SUBSCRIBE */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("alerts"),
.on_message = on_message);
/* Pattern subscription — Redis engine sends PSUBSCRIBE */
fio_pubsub_subscribe(.channel = FIO_BUF_INFO1("events:*"),
.on_message = on_message,
.is_pattern = 1);
/* Publish via Redis — specify the engine to route through Redis PUBLISH.
* Without .engine the default IPC engine is used (local cluster only). */
fio_pubsub_publish(.channel = FIO_BUF_INFO1("alerts"),
.message = FIO_BUF_INFO1("server started"),
.engine = redis_engine);
(void)udata;
}
int main(void) {
redis_engine = fio_redis_new(.url = "localhost:6379");
fio_redis_dup(redis_engine); /* keep a copy before attaching */
fio_pubsub_engine_attach(redis_engine); /* system takes the original ref */
fio_state_callback_add(FIO_CALL_ON_START, on_start, NULL);
fio_io_start(4);
fio_pubsub_engine_detach(redis_engine);
fio_redis_free(redis_engine);
}
```
See [./420 pubsub.md](./420 pubsub.md) for the full Pub/Sub API and engine interface.
---
## Multi-Process Behavior
The IPC routing is automatic and transparent:
| Call | On master | On worker |
|---|---|---|
| `fio_redis_send()` | Queued on the connection | Serialized → IPC → master → Redis → IPC reply → worker callback |
| `fio_pubsub_publish()` | `PUBLISH` through the command queue | Forwarded via IPC; master sends `PUBLISH` |
| Subscription messages | Received as RESP3 push frames, fanned out via Pub/Sub IPC | Delivered by Pub/Sub IPC from master |
Detection uses `fio_io_is_master()`. In single-process mode `fio_io_start(0)` everything uses the master path.
Worker processes inherit the engine pointer after fork. A `FIO_CALL_IN_CHILD` hook swaps the engine's `publish` vtable pointer to the IPC-forwarding implementation, so workers never accidentally write directly to a Redis socket.
See [./404 ipc.md](./404 ipc.md) for IPC internals.
---
## Reconnect and Failure Behavior
- **Connection loss**: The `on_close` callback logs a warning, silently purges any stale queued `HELLO` commands (a fresh one is queued on reconnect), and schedules a reconnect via a 1s single-shot timer. The delay prevents hot reconnect loops against peers that accept and instantly drop.
- **Connection attempt failure**: A connect that fails before attach (e.g., connection refused) is reported through the IO layer's `on_failed` callback (the redis protocol's `on_close` only fires for established connections). The dangling `conn.io` handle is cleared and the same 1s retry timer is scheduled. A synchronous `fio_io_connect` failure takes the same path. The failed io is reactor-owned and freed by the IO layer's connecting-protocol cleanup - the engine never frees it.
- **Handshake**: Every (re)connection starts with `HELLO 3` (auth folded in) as the first queued command; its map reply is consumed by the normal reply FIFO. Handshake failure is a hard engine error (the engine stops; there is no RESP2 fallback).
- **Queued commands**: Commands in the queue on the master stay queued and are flushed once the connection is re-established (after `HELLO`).
- **Resubscription**: On every (re)connection, the engine iterates the current Pub/Sub channel maps and queues all active `SUBSCRIBE` / `PSUBSCRIBE` commands at the queue head (batched into a static buffer, one no-wait queue node per flushed batch), right after `HELLO`. This restores subscription state without touching ref counts or re-attaching the engine.
- **Keepalive**: A `PING` is queued on the connection after `ping_interval` seconds idle. If the connection has unacknowledged commands when the timeout fires, the connection is forcibly closed and reconnected.
- **Engine destroyed while commands are pending**: All queued commands have their callbacks invoked immediately with `reply = FIOBJ_INVALID`.
- **Retry suppression**: Reconnect timers are never scheduled after engine destroy (`running == 0`) or during reactor shutdown, so a close caused by destroy or shutdown does not resurrect the connection.
---
## Thread Safety
All public API functions are thread-safe:
| Function | Mechanism |
|---|---|
| `fio_redis_new()` | Defers connection to IO thread |
| `fio_redis_dup()` | Atomic reference count increment |
| `fio_redis_free()` | Atomic reference count decrement; destroy runs on caller thread |
| `fio_redis_send()` | Defers queue insertion to IO thread (master); IPC on workers |
Internal state (command queue, connection pointers) is only accessed from the IO thread. No locks are needed internally.
FIOBJ objects passed to callbacks are **not** thread-safe. Copy any data you need to keep or use outside the callback.
---
## Limitations
- `fio_redis_new()` must be called before `fio_io_start()` (i.e., before fork). Creating engines from worker processes is not supported.
- **RESP3 required**: Redis >= 6.0 (or a RESP3-capable server such as Valkey). RESP2-only servers and RESP2-only proxies are not supported (the `HELLO 3` handshake fails hard).
- Redis's numeric filter namespaces (`filter` field in Pub/Sub) are not supported. All Redis pub/sub operates with `filter = 0`.
- Single-node Redis only. Redis Cluster requires connecting to the correct shard or using a proxy.
- Replies and push messages are bounded by `payload_limit` (default 16 MiB cumulative per message), not by `FIO_REDIS_READ_BUFFER` — blob strings larger than the read buffer are streamed incrementally.
- Chunked (`$?`) strings are rejected inside push frames (Redis never emits them; the command-reply path supports them).
# HTTP Module
```c
#define FIO_HTTP
#include FIO_INCLUDE_FILE
```
`FIO_HTTP` adds the higher-level HTTP service built on the facil.io IO layer,
the HTTP handle, the HTTP/1.x parser, the WebSocket parser, and the SSE /
WebSocket glue code.
Nearby context: [IO and HTTP overview](./400 io-overview.md), the
[HTTP/1.x parser](./004 http1 parser.md), the
[WebSocket parser](./004 websocket parser.md), and optional compression
support in [DEFLATE / Gzip](./162 deflate.md).
---
## What Gets Added
`FIO_HTTP` exposes:
- `fio_http_settings_s` and `fio_http_listener_s`.
- server APIs: `fio_http_listen`, `fio_http_listener_settings`,
`fio_http_route`, and `fio_http_route_settings`.
- client APIs: `fio_http_connect` and the `fio_http_websocket_connect`
convenience wrapper.
- request routing helper: `fio_http_resource_action` and
`fio_http_resource_action_e`.
- upgrade helpers for WebSocket and SSE connections.
- pub/sub helpers for upgraded connections.
- handle access helpers: `fio_http_io` and `fio_http_settings`.
- the HTTP handle API (handle and core implementation in
`432 http types.h`), including request / response fields, headers,
cookies, body storage, writing, static-file responses, logging, MIME
lookup, and controller hooks.
Implementation helpers named `fio___...` are private implementation details.
---
## Module Files
All HTTP module files share the `FIO_HTTP` flag.
- `430 http api.h` — public declarations: settings, listener / route /
client APIs, and the full handle API.
- `432 http types.h` — internal types plus the handle and core
implementation (header cache, body storage, static-file responses).
- `434 http accept.h` — accept path, request dispatchers, upgrade
authorization.
- `434 http1.h` — HTTP/1.1 request / response glue, protocol and
controller.
- `434 sse.h` — EventSource (SSE) upgrade, helpers, protocol, controller.
- `434 websocket.h` — WebSocket upgrade, events, protocol, write,
controller.
- `438 http.h` — listen / connect glue, protocol wiring, shared helpers.
- `439 http.h` — cleanup tail (the module's only `#undef FIO_HTTP` site).
The parsers are separate modules: [`004 http1 parser.h`](./004 http1 parser.md)
and [`004 websocket parser.h`](./004 websocket parser.md).
---
## Connection Model
HTTP/1.x requests and client responses are parsed into `fio_http_s` handles.
A response line received by a server connection (or an unsolicited response
received by a client) is logged as a `SECURITY` event and the connection is
closed.
Malformed requests receive a `400 Bad Request` response and the connection is
closed. This includes request framing that could be misread as a pipelined
(smuggled) request: a `Transfer-Encoding` without a single, final `chunked`
coding (RFC 9112 §6.3, §7), and an HTTP/1.1 request without exactly one `Host`
header (RFC 9112 §3.2). Other transfer codings before the final `chunked`
(i.e., `gzip, chunked`) stay visible to the application as a request
`transfer-encoding` header (i.e., `gzip`); the body is passed through as
received, still encoded.
The HTTP layer suspends the IO object while the user callback is running and
resumes it after the response is finished or the upgrade is installed.
Plain HTTP cycles use:
1. request / response parsing;
2. optional `pre_http_body` before an `Expect: 100-continue` body is read;
3. `on_http` for application logic;
4. `fio_http_write` / `fio_http_finish` to send the response;
5. `on_finish` when the non-upgraded cycle is complete.
For non-upgraded server HTTP/1 cycles, a request `Connection` field containing
the case-insensitive `close` token closes after the complete response drains;
subsequent pipelined requests are not dispatched. HTTP/1.0 requests are not
persistent unless a request `Connection` field contains `keep-alive` (RFC 9112
§9.3); the server confirms persistence with a `connection: keep-alive`
response header (unless the application set a `connection` header). Application-initiated closure
remains explicit: schedule the complete response, then call `fio_http_close`
while the handle is valid (i.e., from `on_finish`). Setting only a response
`Connection: close` header does not itself schedule closure.
WebSocket and SSE cycles use the authentication callbacks before upgrade, then
`on_open`, message / event callbacks, `on_ready`, `on_shutdown`, `on_close`, and
finally `on_finish` when the upgraded connection closes.
User callbacks are scheduled through the selected HTTP task queue. If
`fio_http_settings_s.queue` is not supplied, the current IO queue is used.
---
## Settings and Listener API
### `fio_http_settings_s`
```c
typedef struct fio_http_settings_s fio_http_settings_s;
```
`fio_http_settings_s` is used by server listeners, routes, and client
connections. The function-style APIs are shadowed by named-argument macros, so
settings are usually passed as designated initializers.
Callback fields:
| Field | Used for |
| --- | --- |
| `pre_http_body` | Called before body upload when the request used `Expect`. |
| `on_http` | Server request callback, or client response callback. |
| `on_finish` | Called when a plain request / response cycle completes, and after upgraded close cleanup. |
| `on_authenticate_sse` | Return non-zero to reject an SSE upgrade. |
| `on_authenticate_websocket` | Return non-zero to reject a WebSocket upgrade. |
| `on_open` | Called once a WebSocket or SSE upgrade is complete. |
| `on_message` | Called with complete WebSocket messages. |
| `on_eventsource` | Called with complete SSE events. |
| `on_eventsource_reconnect` | Called for SSE requests with `Last-Event-ID`. |
| `on_ready` | Called when an upgraded connection's outgoing buffer is empty. |
| `on_shutdown` | Called for open upgraded connections during IO shutdown. |
| `on_close` | Called after an upgraded connection is closed. |
| `on_stop` | Called when HTTP service settings are being closed. |
Other settings:
| Field | Used for |
| --- | --- |
| `udata` | Default `fio_http_udata(h)` value. |
| `tls_io_func`, `tls` | Optional TLS support. |
| `queue` | Optional HTTP task queue. |
| `public_folder` | Static-file root. Serves pre-compressed `.br`, `.zstd`, `.gz`, `.zip` variants when the client accepts them and the variant is fresh. With `compress_static`, missing `.br`/`.gz` variants are also **created on demand and written into this folder** — the folder must be writable and quota'd (attacker-triggerable disk writes). Prefer pre-generating variants at deploy time. The folder must exist when the listener starts, otherwise the setting is ignored. See *Static File Service* below. |
| `max_age` | Static-file `Cache-Control` max-age value, in seconds. `0` (default) omits the header. Not inherited by routes. |
| `max_header_size` | Maximum combined request line and header bytes. |
| `max_line_len` | Maximum bytes per request / header line. |
| `max_body_size` | Maximum request body size. |
| `ws_max_msg_size` | Maximum buffered WebSocket message size. |
| `reserved1`, `reserved2` | Reserved for future use. |
| `timeout` | HTTP/1.x connection timeout, in seconds. |
| `ws_timeout` | WebSocket timeout; timeout pings are sent. |
| `sse_timeout` | SSE timeout; timeout pings are sent. |
| `connect_timeout` | Client connection timeout. |
| `log` | Enables HTTP request logging. |
| `compress_static` | Opt-in static-file compression (on-demand `.br`/`.gz` creation). Per-route; routes inherit the listener's root value at route-creation. The value is a failure-memoization shift register (see *Static File Service*). Detached handles gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` cflag instead. See the compression security note below. |
| `compress_dynamic` | Opt-in dynamic response compression. Connection-global; per-route values are ignored. See the compression security note below. |
| `compress_ws` | Opt-in WebSocket `permessage-deflate`. Connection-global; per-route values are ignored. See the compression security note below. |
**Compression security note (BREACH/CRIME oracle risk):** compressing
secrets together with attacker-reflected data enables length-oracle attacks
(BREACH/CRIME). Do not enable compression for responses that mix secrets
(CSRF tokens, session data) with attacker-controlled reflections, or emit
such messages uncompressed per call (per-message opt-out: keep the cflag off
at the route level for sensitive routes, or pre-set `content-encoding`
— which the dynamic path always respects — to pass a body through
uncompressed).
**WebSocket `permessage-deflate` negotiation:** the server ALWAYS responds
with `permessage-deflate; server_no_context_takeover;
client_no_context_takeover` (RFC 7692 allows either endpoint to request the
flags unilaterally), honoring `server_max_window_bits` when offered (the
compressor clamps its distances accordingly) and never emitting window-bits
parameters. Cross-message history is never used, so persistent compression
state per connection is ~0 (two ~32-byte contexts plus a bounded input
buffer, shrunk on every message reset); compressor scratch comes from a
static process-wide slot pool checked out per call — under extreme
contention a message simply goes out uncompressed.
**Accept-Encoding handling:** `q=0` (in any zero-padded form) forbids an
encoding (RFC 9110). `Vary: accept-encoding` is set whenever compressed
variants may exist — including on identity responses. Ranged responses are
never compressed (ranges over encoded bytes are broken in practice).
Unset callbacks and limits are normalized when the listener, route, or client is
created. Server `on_http` defaults to a 404 response; client `on_http` defaults
to a no-op. Server upgrade authentication defaults to denial. Client upgrade
authentication defaults to allow.
### `fio_http_listen`
```c
fio_http_listener_s *fio_http_listen(const char *url,
fio_http_settings_s settings);
#define fio_http_listen(url, ...) \
fio_http_listen(url, (fio_http_settings_s){__VA_ARGS__})
```
Starts an HTTP / WebSocket / SSE listener on `url`.
```c
fio_http_listener_s *l = fio_http_listen("0.0.0.0:3000",
.on_http = on_request,
.log = 1);
```
The listener is rooted at `/`; a path component in the listen URL is ignored.
Use `fio_http_route` for per-prefix behavior.
### `fio_http_listener_settings`
```c
fio_http_settings_s *fio_http_listener_settings(fio_http_listener_s *l);
```
Returns the listener's root settings object. Prefer `fio_http_route` for
changing root routing behavior after the listener is created.
### `FIO_HTTP_AUTHENTICATE_ALLOW`
```c
int FIO_HTTP_AUTHENTICATE_ALLOW(fio_http_s *h);
```
Authentication callback that always allows the upgrade. Use it for
`.on_authenticate_sse` or `.on_authenticate_websocket` when no gate is needed.
### `fio_http_io` and `fio_http_settings`
```c
fio_io_s *fio_http_io(fio_http_s *h);
fio_http_settings_s *fio_http_settings(fio_http_s *h);
```
`fio_http_io` returns the IO object attached to the handle, when one exists.
`fio_http_settings` returns the route settings matching the handle's original
path, when connection data is available.
---
## Routing
### Prefix routes
```c
int fio_http_route(fio_http_listener_s *listener,
const char *url,
fio_http_settings_s settings);
#define fio_http_route(listener, url, ...) \
fio_http_route(listener, url, (fio_http_settings_s){__VA_ARGS__})
fio_http_settings_s *fio_http_route_settings(fio_http_listener_s *l,
const char *url);
```
Routes are best-prefix matches:
- `/` matches everything.
- `/user` matches `/user` and `/user/...`, but not `/user...`.
- More specific prefixes such as `/user/new` win over `/user`.
Route declaration order is not significant unless an existing route is replaced.
Route settings inherit missing listener callbacks and limits, including `udata`,
`on_finish`, `on_stop`, SSE / WebSocket authentication callbacks,
header/body/message limits, timeouts, `public_folder`, `compress_static`, and
`log`. TLS settings are ignored on routes.
Only `on_http`, `on_finish`, `udata`, the authentication callbacks,
`public_folder`, `max_age`, and `compress_static` are effective per route.
Upgraded-connection callbacks (`on_open`, `on_message`, `on_ready`,
`on_shutdown`, `on_close`), `queue`, `log`, limits, timeouts,
`compress_dynamic`, and `compress_ws` are connection-global: they are read
from the listener's root settings, so per-route values are stored but unused.
Note that `max_age` is **not** inherited — a route that serves static files
must set its own.
### Resource action helper
```c
typedef enum {
FIO_HTTP_RESOURCE_NONE,
FIO_HTTP_RESOURCE_INDEX,
FIO_HTTP_RESOURCE_SHOW,
FIO_HTTP_RESOURCE_NEW,
FIO_HTTP_RESOURCE_EDIT,
FIO_HTTP_RESOURCE_CREATE,
FIO_HTTP_RESOURCE_UPDATE,
FIO_HTTP_RESOURCE_DELETE,
FIO_HTTP_RESOURCE_QUERY,
} fio_http_resource_action_e;
fio_http_resource_action_e fio_http_resource_action(fio_http_s *h);
```
Maps the current method and routed path to a small REST-style action:
| Action | Request shape |
| --- | --- |
| `FIO_HTTP_RESOURCE_INDEX` | `GET /` |
| `FIO_HTTP_RESOURCE_SHOW` | `GET /:id` |
| `FIO_HTTP_RESOURCE_NEW` | `GET /new` |
| `FIO_HTTP_RESOURCE_EDIT` | `GET /:id/edit` |
| `FIO_HTTP_RESOURCE_CREATE` | `PUT`, `POST`, or `PATCH /` |
| `FIO_HTTP_RESOURCE_UPDATE` | `PUT`, `POST`, or `PATCH /:id` |
| `FIO_HTTP_RESOURCE_DELETE` | `DELETE /:id` |
| `FIO_HTTP_RESOURCE_QUERY` | `QUERY` (any path) |
`FIO_HTTP_RESOURCE_NONE` is returned for unsupported shapes or invalid input.
---
## Static File Service
When the matching route has a `public_folder`, the module attempts a static
file response (via `fio_http_static_file_response`) for `GET` and `HEAD`
requests **before** calling `on_http`. All other methods (`POST`, `PUT`,
`DELETE`, `OPTIONS`, ...) fall through to `on_http` untouched — a static
file is not a valid response to them (RFC 9110 §9.3) — as do misses (not
found or unsafe path), with the status pre-set to 200.
For the listener's root `public_folder` the full original path is appended
to the folder; for a route-level `public_folder` the routed (prefix-trimmed)
path is appended instead. Applications can also call
`fio_http_static_file_response` directly from `on_http` — the API is
method-agnostic (except `OPTIONS`, which it refuses), so the application
can answer any other method with a file when it chooses to.
### Path resolution and safety
- The request path is URL-decoded and joined to the root folder; paths
folding backwards (`/../`, `//`) are rejected.
- A path resolving to a folder is retried as the folder's index file
(`FIO_HTTP_DEFAULT_INDEX_FILENAME`, `"index"` by default).
- With `FIO_HTTP_STATIC_FILE_COMPLETION` (default `1`), unresolved paths are
retried with `.html`, `.htm`, `.txt`, and `.md` appended (so a folder
resolves to `index.html`, `index.htm`, ...). Only regular files and
symlinks are served.
- The response `Content-Type` comes from the MIME registry
(`fio_http_mimetype`); an unregistered extension logs a warning.
### Pre-compressed variants
Unless the request is ranged (see below), the `Accept-Encoding` header is
tested in preference order **`br` → `zstd` → `gzip` → `deflate`**, mapping
to `.br`, `.zstd`, `.gz`, and `.zip` next to the
original. The first accepted variant that exists on disk **and** is at
least as fresh as the original (modification time) is served, with
`Content-Encoding` set accordingly. Tokens forbidden with `q=0` are never
selected (RFC 9110 §12.5.3).
With `compress_static` enabled, missing or stale `.br` / `.gz` variants are
created on demand and written into the root folder. Attached handles read the
flag from the matching route's settings (routes inherit the listener's root
value at route-creation); detached handles (created manually, with no
settings) gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` handle cflag instead.
- creation is limited to compressible (text-like) MIME types and originals
between 1024 bytes and `FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT` (2 MiB);
- creation is skipped when compression would not shrink the file;
- `.br` variants are compressed at brotli quality 4 (the benchmark-reviewed
fast-path ceiling; q5+ engages the slow hash-chain path) and `.gz`
variants at gzip level 6;
- `zstd` / `deflate` variants are only ever served pre-generated — they are
never created on demand;
- on-demand creation writes into the served folder and is triggered by
client requests; the folder must be writable for creation to succeed
(a write failure disables creation, see memoization below). Variants
may also be pre-generated at deploy time with any external tool — the
server only stats the files.
`compress_static` is a failure-memoization shift register, updated atomically
(the IO threads share the listener / route settings): any non-zero value
enables on-demand creation. A compression success re-seeds bit 0
(`value |= 1`); any other failure shifts the value left (`value <<= 1`), so
8 consecutive failures shift out of the `uint8_t` and disable creation, as
does a single filesystem-full / permission error (`ENOSPC`, `EACCES`,
`EROFS`, `EDQUOT` — `value = 0`). Once 0, creation stays disabled until the
settings are re-applied. Detached handles carry no memoization state.
`Vary: accept-encoding` is set whenever a variant is served, and on
identity responses whenever variants may be created, so caches key on the
client's `Accept-Encoding`.
### Conditional, ranged, and HEAD requests
- The `ETag` is a hash of the served file's `stat` data (it changes when
the file changes). A matching `If-None-Match` (GET/HEAD) yields a `304`.
- `Last-Modified` reflects the file's modification time;
`Cache-Control: max-age=` is only sent when `max_age` is
non-zero.
- Single `Range: bytes=start-end` requests are supported (`206` with
`Content-Range`), including open-ended (`bytes=N-`) and suffix
(`bytes=-N`) forms. `If-Range` must equal the current `ETag` or the
range is ignored. Unsatisfiable ranges get `416` with
`Content-Range: bytes */`. Ranged responses are always identity
(no variant selection) and carry no `ETag`. `Accept-Ranges: bytes` is
always sent.
- `HEAD` requests pass through the same logic and finish with headers only.
---
## HTTP Client Connections
```c
fio_io_s *fio_http_connect(const char *url,
fio_http_s *h,
fio_http_settings_s settings);
#define fio_http_connect(url, h, ...) \
fio_http_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects as an HTTP / WebSocket / SSE client. If `h` is `NULL`, a new handle is
created. Missing method, path, query, and `Host` are filled from `url`.
Recognized upgrade schemes:
- `ws://` and `wss://` call `fio_http_websocket_set_request`.
- `sse://` and `sses://` call `fio_http_sse_set_request`.
Client `on_http` receives the response handle. If the response accepts a
WebSocket or SSE upgrade, the connection switches to the upgraded protocol and
uses the upgraded callbacks instead.
Interim `1xx` responses (i.e., `100 Continue`, `103 Early Hints`) other than
`101` are skipped (RFC 9110 §15.2): `on_http` runs once, for the final
response, and interim headers are discarded. Responses to `HEAD` requests, and
`204` / `304` responses, never read a body, even if they carry
`content-length` or `transfer-encoding` headers. Those headers stay visible
as response headers.
### `fio_http_websocket_connect`
```c
fio_io_s *fio_http_websocket_connect(const char *url,
fio_http_s *h,
fio_http_settings_s settings);
#define fio_http_websocket_connect(url, h, ...) \
fio_http_websocket_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to a WebSocket server. A convenience wrapper around
`fio_http_connect` that normalizes the `url` scheme before connecting:
`http://` becomes `ws://`, `https://` becomes `wss://`, and a missing scheme
defaults to `ws://` (`ws://` / `wss://` and any other scheme pass through
unchanged).
`fio_http_connect` issues the WebSocket upgrade request automatically for
`ws://` / `wss://` URLs. If the server accepts the upgrade (`101`), the
connection switches to the WebSocket protocol and the `on_open` /
`on_message` / `on_close` callbacks are used. Any other response is routed
to `settings.on_http` with the response handle.
---
## WebSocket and SSE Upgrades
### Upgrade tests and request / response setup
These helpers are declared by the HTTP handle and used by the HTTP module:
```c
int fio_http_websocket_requested(fio_http_s *h);
int fio_http_websocket_accepted(fio_http_s *h);
void fio_http_upgrade_websocket(fio_http_s *h);
void fio_http_websocket_set_request(fio_http_s *h);
int fio_http_sse_requested(fio_http_s *h);
int fio_http_sse_accepted(fio_http_s *h);
void fio_http_upgrade_sse(fio_http_s *h);
void fio_http_sse_set_request(fio_http_s *h);
```
`*_requested` inspects request headers. `*_accepted` inspects client-side
response state and marks the handle as upgraded on success. `fio_http_upgrade_*`
sets the server response state for the upgrade. `*_set_request` prepares a
client request handle.
Server listeners normally call the authentication callbacks and then upgrade
automatically when a request asks for WebSocket or SSE.
### WebSocket write and callback override
```c
int fio_http_websocket_write(fio_http_s *h,
const void *buf,
size_t len,
uint8_t is_text);
int fio_http_on_message_set(fio_http_s *h,
void (*on_message)(fio_http_s *,
fio_buf_info_s,
uint8_t));
```
`fio_http_websocket_write` writes one WebSocket message and fails if the handle
is not an established WebSocket. `is_text` selects text vs. binary.
**Serialization:** `fio_http_websocket_write` must be serialized per
connection — concurrent calls on the same handle (e.g. from pub/sub or queue
callbacks racing on multiple threads) may interleave frame bytes and must be
guarded by the caller (mutex, or route all writes through the connection's
queue). This also protects the per-connection compression context.
`fio_http_on_message_set` overrides the `on_message` callback for the current
WebSocket connection. Passing `NULL` restores the settings callback. It returns
`-1` when the handle is not ready for this change.
Incoming WebSocket messages are accumulated up to `ws_max_msg_size` and then
scheduled on the HTTP queue. Control frames are handled by the module.
### SSE write
```c
typedef struct {
fio_buf_info_s id;
fio_buf_info_s event;
fio_buf_info_s data;
} fio_http_sse_write_args_s;
int fio_http_sse_write(fio_http_s *h, fio_http_sse_write_args_s args);
#define fio_http_sse_write(h, ...) \
fio_http_sse_write((h), ((fio_http_sse_write_args_s){__VA_ARGS__}))
```
Writes one UTF-8 SSE event. `data` is required. `id` and `event` are optional.
Multiline data is emitted as repeated `data:` lines. The helper fails if the
handle is not an established SSE connection.
### Pub/sub helpers
```c
#define fio_http_subscribe(h, ...) \
fio_pubsub_subscribe(.io = fio_http_io(h), __VA_ARGS__)
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT(fio_pubsub_msg_s *msg);
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT_TEXT(fio_pubsub_msg_s *msg);
void FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT_BINARY(fio_pubsub_msg_s *msg);
void FIO_HTTP_SSE_SUBSCRIBE_DIRECT(fio_pubsub_msg_s *msg);
```
`fio_http_subscribe` subscribes using the handle's IO object.
The direct WebSocket callbacks write published messages to the WebSocket. The
plain `FIO_HTTP_WEBSOCKET_SUBSCRIBE_DIRECT` helper checks short payloads for
UTF-8 and selects text or binary; the explicit `_TEXT` and `_BINARY` variants
skip that guess.
`FIO_HTTP_SSE_SUBSCRIBE_DIRECT` writes an SSE event whose event name is the
channel and whose data is the published message.
---
## HTTP Handle API Pulled In by `FIO_HTTP`
The HTTP handle is the request / response object exposed to callbacks.
### Lifetime
```c
typedef struct fio_http_s fio_http_s;
fio_http_s *fio_http_new(void);
fio_http_s *fio_http_new_copy_request(fio_http_s *old);
void fio_http_free(fio_http_s *h);
fio_http_s *fio_http_dup(fio_http_s *h);
fio_http_s *fio_http_destroy(fio_http_s *h);
void fio_http_start_time_set(fio_http_s *h);
fio_http_s *fio_http_clear_response(fio_http_s *h, bool clear_body);
void fio_http_close(fio_http_s *h);
```
`fio_http_s` is reference-counted. It is not designed as a thread-safe mutable
object. `fio_http_close` closes the associated connection, including an ordinary
HTTP/1 connection. Schedule the complete response before calling it; previously
scheduled output drains before closure.
### User data and controller data
```c
void *fio_http_udata(fio_http_s *h);
void *fio_http_udata_set(fio_http_s *h, void *udata);
void *fio_http_udata2(fio_http_s *h);
void *fio_http_udata2_set(fio_http_s *h, void *udata);
void *fio_http_cdata(fio_http_s *h);
void *fio_http_cdata_set(fio_http_s *h, void *cdata);
fio_http_controller_s *fio_http_controller(fio_http_s *h);
fio_http_controller_s *fio_http_controller_set(fio_http_s *h,
fio_http_controller_s *c);
```
`udata` and `udata2` are for application state. `cdata` and the controller are
used by the HTTP layer and custom controllers.
### Request / response properties
```c
size_t fio_http_status(fio_http_s *h);
size_t fio_http_status_set(fio_http_s *h, size_t status);
fio_str_info_s fio_http_method(fio_http_s *h);
fio_str_info_s fio_http_method_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_opath(fio_http_s *h);
fio_str_info_s fio_http_opath_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_path(fio_http_s *h);
fio_str_info_s fio_http_path_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_query(fio_http_s *h);
fio_str_info_s fio_http_query_set(fio_http_s *h, fio_str_info_s value);
fio_str_info_s fio_http_version(fio_http_s *h);
fio_str_info_s fio_http_version_set(fio_http_s *h, fio_str_info_s value);
int64_t fio_http_received_at(fio_http_s *h);
int64_t fio_http_received_at_set(fio_http_s *h, int64_t value);
```
`opath` is the original path before route prefix trimming. `path` is the routed
path visible to the current callback.
`fio_http_status_set(h, 0)` normalizes to `200`; values above the internal range
normalize to `500`.
### State checks and flags
```c
int fio_http_is_clean(fio_http_s *h);
int fio_http_is_finished(fio_http_s *h);
int fio_http_is_streaming(fio_http_s *h);
int fio_http_is_upgraded(fio_http_s *h);
int fio_http_is_websocket(fio_http_s *h);
int fio_http_is_sse(fio_http_s *h);
int fio_http_is_freeing(fio_http_s *h);
```
The handle also exposes inline flag helpers generated for `cflags` and
`uflags`:
```c
uint16_t fio_http_cflags(fio_http_s *h);
uint16_t fio_http_cflags_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_unset(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_flip(fio_http_s *h, uint16_t flags);
uint16_t fio_http_cflags_is_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags(fio_http_s *h);
uint16_t fio_http_uflags_set(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_unset(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_flip(fio_http_s *h, uint16_t flags);
uint16_t fio_http_uflags_is_set(fio_http_s *h, uint16_t flags);
```
Prefer the state helpers unless implementing protocol or controller glue.
### Headers
```c
fio_str_info_s fio_http_request_header(fio_http_s *h,
fio_str_info_s name,
size_t index);
size_t fio_http_request_header_count(fio_http_s *h, fio_str_info_s name);
fio_str_info_s fio_http_request_header_set(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
fio_str_info_s fio_http_request_header_set_if_missing(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
fio_str_info_s fio_http_request_header_add(fio_http_s *h,
fio_str_info_s name,
fio_str_info_s value);
size_t fio_http_request_header_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s name,
fio_str_info_s value,
void *udata),
void *udata);
```
Response headers use the same shape with `response` in the function name:
```c
fio_http_response_header;
fio_http_response_header_count;
fio_http_response_header_set;
fio_http_response_header_set_if_missing;
fio_http_response_header_add;
fio_http_response_header_each;
```
`*_header` returns an empty value if the requested value is missing. Use
`index` for repeated values. If `name.buf == NULL`, `*_header_count` returns
the number of unique header names.
Header insertion rejects names longer than 4095 bytes and names containing the
parser's forbidden name bytes (including whitespace, colon, NUL, CR, and LF).
Values containing NUL, CR, or LF are rejected. Invalid insertion returns an empty
value without replacing an existing header. With
`FIO_HTTP_ENFORCE_LOWERCASE_HEADERS`, names are validated and copied to ASCII
lowercase in one pass; caller-owned input is not modified.
Response headers cannot be changed after the response headers are sent.
### Header value and property iteration
```c
FIO_HTTP_HEADER_EACH_VALUE(h, 1, FIO_STR_INFO1("accept"), item) {
/* item.name and item.value are length-delimited fio_buf_info_s slices. */
FIO_HTTP_HEADER_EACH_PROPERTY(item, prop) {
/* prop.name and prop.value are length-delimited slices. */
}
}
```
The macros iterate repeated fields, comma-separated values and semicolon-separated
properties without copying. They trim surrounding spaces and tabs; quoted
strings remain quoted, including escapes. Slices borrow header storage and are
not necessarily NUL-terminated: use their `.len` rather than string functions.
Do not change headers during iteration. `value.buf == NULL` marks the end;
empty property values may have a non-NULL buffer and zero length. The helpers
`fio_http_header_each_first` / `fio_http_header_each_next` and
`fio_http_header_each_property_first` / `fio_http_header_each_property_next`
can also be used directly.
### Body storage and reads
```c
size_t fio_http_body_length(fio_http_s *h);
size_t fio_http_body_pos(fio_http_s *h);
size_t fio_http_body_seek(fio_http_s *h, ssize_t pos);
fio_str_info_s fio_http_body_read(fio_http_s *h, size_t length);
fio_str_info_s fio_http_body_read_until(fio_http_s *h,
char token,
size_t limit);
void fio_http_body_expect(fio_http_s *h, size_t expected_length);
void fio_http_body_write(fio_http_s *h, const void *data, size_t len);
int fio_http_body_fd(fio_http_s *h);
void fio_http_body_close(fio_http_s *h);
```
The handle stores body data in RAM until the configured threshold is crossed,
then uses a temporary file. `fio_http_body_fd` returns that file descriptor or
`-1`.
`fio_http_body_close` releases the body's RAM buffer and temporary file (if
any) and resets the body to empty. The body is also released automatically
when the handle is cleared or destroyed, so calling this is only needed to
free resources early, once the body has been consumed.
### Body parsing
```c
typedef struct fio_http_body_parse_callbacks_s fio_http_body_parse_callbacks_s;
typedef struct fio_http_body_parse_result_s fio_http_body_parse_result_s;
fio_http_body_parse_result_s fio_http_body_parse(
fio_http_s *h,
const fio_http_body_parse_callbacks_s *callbacks,
void *udata);
```
The body parser auto-detects JSON, URL-encoded, and multipart/form-data input.
Callbacks build application objects for primitives, arrays, maps, and file
uploads.
`fio_http_body_parse_callbacks_s` contains primitive callbacks (`on_null`,
`on_true`, `on_false`, `on_number`, `on_float`, `on_string`), container
callbacks (`on_array`, `on_map`, `array_push`, `map_set`, `array_done`,
`map_done`), multipart callbacks (`on_file`, `on_file_data`, `on_file_done`),
and error / cleanup callbacks (`on_error`, `free_unused`).
`fio_http_body_parse_result_s` reports the top-level result, consumed bytes,
and error code.
### Cookies
```c
typedef enum fio_http_cookie_same_site_e {
FIO_HTTP_COOKIE_SAME_SITE_BROWSER_DEFAULT = 0,
FIO_HTTP_COOKIE_SAME_SITE_NONE,
FIO_HTTP_COOKIE_SAME_SITE_LAX,
FIO_HTTP_COOKIE_SAME_SITE_STRICT,
} fio_http_cookie_same_site_e;
typedef struct fio_http_cookie_args_s fio_http_cookie_args_s;
int fio_http_cookie_set(fio_http_s *h, fio_http_cookie_args_s args);
#define fio_http_cookie_set(h, ...) \
fio_http_cookie_set((h), (fio_http_cookie_args_s){__VA_ARGS__})
fio_str_info_s fio_http_cookie(fio_http_s *h,
const char *name,
size_t name_len);
size_t fio_http_cookie_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s name,
fio_str_info_s value,
void *udata),
void *udata);
size_t fio_http_set_cookie_each(fio_http_s *h,
int (*callback)(fio_http_s *,
fio_str_info_s set_cookie,
fio_str_info_s value,
void *udata),
void *udata);
```
`fio_http_cookie_args_s` contains `name`, `value`, optional `domain` and `path`,
`max_age`, `same_site`, and the `secure`, `http_only`, and `partitioned` flags.
An empty / missing value deletes the cookie by setting a negative max-age.
### Writing responses
```c
typedef struct fio_http_write_args_s {
const void *buf;
size_t len;
size_t offset;
int fd;
void (*dealloc)(void *);
int copy;
int finish;
} fio_http_write_args_s;
void fio_http_write(fio_http_s *h, fio_http_write_args_s args);
#define fio_http_write(h, ...) fio_http_write(h, (fio_http_write_args_s){__VA_ARGS__})
#define fio_http_finish(h) fio_http_write(h, .finish = 1)
```
`fio_http_write` sends headers on the first write and then writes body data. If
`finish` is set, the response is complete. Without `finish`, the response is a
stream. A finished response without an explicit `content-length` header gets
one automatically. The response starts with the first write that carries body
data (a buffer or a file) or `finish`: an empty write without `finish` is a
no-op, so headers can still be set, and a response that is finished without
any body data is sent with `content-length: 0` rather than as an empty
chunked stream. HTTP/1.1 streams use `transfer-encoding: chunked`; HTTP/1.0 responses
never use a transfer coding (RFC 9112 §6.1), so an HTTP/1.0 stream is sent
unframed and delimited by closing the connection, even if `keep-alive` was
requested.
Responses without content (`1xx`, `204`, `205`, `304`, RFC 9110 §15) never
send body data, are never streamed or compressed, and never send
`transfer-encoding` (an application-provided value is removed). Their
`content-length` follows RFC 9110 §8.6:
- `1xx` / `204`: removed, never added.
- `205`: always `content-length: 0` (§15.3.6), replacing any other value.
- `304`: an application-provided value is kept (it describes the selected
representation); none is added automatically.
Responses to `HEAD` requests send the headers a `GET` would (a finished
response still gets its automatic `content-length`), but never send content
(RFC 9110 §9.3.2): body writes are discarded and the response is never
streamed (no `transfer-encoding: chunked`). File writes use `fd` and `offset`; the file descriptor is always closed
by the write path.
On upgraded handles, `fio_http_write` routes through the WebSocket or SSE
controller. For WebSocket text/binary choice, call `fio_http_websocket_write`
directly when the payload type matters.
### General helpers
```c
int fio_http_send_error_response(fio_http_s *h, size_t status);
int fio_http_etag_is_match(fio_http_s *h);
int fio_http_static_file_response(fio_http_s *h,
fio_str_info_s root_folder,
fio_str_info_s file_name,
size_t max_age);
fio_str_info_s fio_http_status2str(size_t status);
void fio_http_write_log(fio_http_s *h);
int fio_http_from(fio_str_info_s *dest, const fio_http_s *h);
fio_str_info_s fio_http_date(uint64_t now_in_seconds);
fio_str_info_s fio_http_log_time(uint64_t now_in_seconds);
int64_t fio_http_get_timestump(void);
```
`fio_http_get_timestump` is the inline timestamp helper used by the handle for
`received_at` and log timing. User code usually reads timestamps with
`fio_http_received_at` or formats them with `fio_http_date` /
`fio_http_log_time`.
`fio_http_from` writes a best-effort peer address starting with the `Forwarded`
header, then socket peer address, then `"[unknown]"`.
`fio_http_static_file_response` sends a file from a root folder and finishes
the response on success (0), returning -1 when the application should handle
the request itself. See *Static File Service* for the path resolution,
pre-compressed variant, range, and caching semantics.
### Path and MIME helpers
```c
#define FIO_HTTP_PATH_EACH(path, pos)
int fio_http_mimetype_register(char *file_ext,
size_t file_ext_len,
fio_str_info_s mime_type);
fio_str_info_s fio_http_mimetype(char *file_ext, size_t file_ext_len);
```
`FIO_HTTP_PATH_EACH` iterates decoded path sections using a 4096-byte stack
buffer. The MIME registry is not thread-safe.
### Controller
```c
typedef struct fio_http_controller_s fio_http_controller_s;
struct fio_http_controller_s {
uintptr_t private_flags;
void (*on_destroyed)(fio_http_s *h);
void (*send_headers)(fio_http_s *h);
void (*write_body)(fio_http_s *h, fio_http_write_args_s args);
void (*on_finish)(fio_http_s *h);
void (*close_io)(fio_http_s *h);
int (*get_fd)(fio_http_s *h);
};
```
Controllers allow the handle to write through different protocols. Initialize
custom controllers to zero before use. If controller callbacks are not
thread-safe, call `fio_http_write` only from the controller's expected thread.
---
## Compile-Time Knobs
HTTP module defaults:
```c
FIO_HTTP_DEFAULT_MAX_HEADER_SIZE /* 32768 */
FIO_HTTP_DEFAULT_MAX_LINE_LEN /* 8192 */
FIO_HTTP_DEFAULT_MAX_BODY_SIZE /* 33554432 */
FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE /* 262144 */
FIO_HTTP_DEFAULT_TIMEOUT /* 50 */
FIO_HTTP_DEFAULT_TIMEOUT_LONG /* 50 */
FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER /* 0 */
FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT
FIO_WEBSOCKET_STATS /* 0 */
FIO_HTTP_WEBSOCKET_DEFLATE_MIN /* 1024 */
```
HTTP handle defaults:
```c
FIO_HTTP_EXACT_LOGGING /* fuzzy timestamps by default with IO */
FIO_HTTP_BODY_RAM_LIMIT /* 1 << 17 */
FIO_HTTP_CACHE_LIMIT /* 0 */
FIO_HTTP_CACHE_STR_MAX_LEN /* 1 << 12 */
FIO_HTTP_CACHE_USES_MUTEX /* 1 */
FIO_HTTP_PRE_CACHE_KNOWN_HEADERS /* 1 */
FIO_HTTP_DEFAULT_INDEX_FILENAME /* "index" */
FIO_HTTP_STATIC_FILE_COMPLETION /* 1 */
FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT /* 1 << 21 */
FIO_HTTP_LOG_X_REQUEST_START /* 1 */
FIO_HTTP_ENFORCE_LOWERCASE_HEADERS /* 0 */
```
State and controller flag macros are exposed for protocol glue:
```c
FIO_HTTP_STATE_STREAMING
FIO_HTTP_STATE_FINISHED
FIO_HTTP_STATE_UPGRADED
FIO_HTTP_STATE_WEBSOCKET
FIO_HTTP_STATE_SSE
FIO_HTTP_STATE_COOKIES_PARSED
FIO_HTTP_STATE_FREEING
FIO_HTTP_CFLAG_COMPRESS_DYNAMIC
FIO_HTTP_CFLAG_COMPRESS_WS
FIO_HTTP_CFLAG_COMPRESS_STATIC
```
Prefer public helper functions over reading state bits directly in application
code.
---
## Minimal Server
```c
#define FIO_HTTP
#include FIO_INCLUDE_FILE
static void on_http(fio_http_s *h) {
fio_http_response_header_set(h,
FIO_STR_INFO1("content-type"),
FIO_STR_INFO1("text/plain"));
fio_http_write(h, .buf = "hello\n", .len = 6, .finish = 1);
}
void start_http(void) {
fio_http_listen("0.0.0.0:3000", .on_http = on_http, .log = 1);
}
```
For WebSocket or SSE routes, provide the relevant authentication and upgraded
connection callbacks in the route settings.
## Hash Function Testing
During development I tested the Hash functions using [the SMHasher testing suite (@rurban's fork)](https://github.com/rurban/smhasher). The testing suite is often growing with more tests, so I do not know what the future may bring... but attached are the test results for Risky Hash, Stable Hash (64 and 128 bit variation) as they were at the time of (re)development (March, 2023).
### Risky Hash SMHasher Results
The following results were achieved on my personal computer when testing the facil.io Risky Hash (`fio_risky_hash`).
```txt
------------------------------------------------------------
--- Testing Risky "facil.io Risky Hash" GOOD
[[[ Sanity Tests ]]]
Verification value 0x407D2C05 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 17.861 bytes/cycle - 51100.77 MiB/sec @ 3 ghz
Alignment 6 - 17.833 bytes/cycle - 51019.28 MiB/sec @ 3 ghz
Alignment 5 - 17.832 bytes/cycle - 51018.87 MiB/sec @ 3 ghz
Alignment 4 - 17.853 bytes/cycle - 51077.60 MiB/sec @ 3 ghz
Alignment 3 - 17.844 bytes/cycle - 51051.63 MiB/sec @ 3 ghz
Alignment 2 - 17.854 bytes/cycle - 51080.37 MiB/sec @ 3 ghz
Alignment 1 - 17.912 bytes/cycle - 51245.99 MiB/sec @ 3 ghz
Alignment 0 - 19.064 bytes/cycle - 54541.55 MiB/sec @ 3 ghz
Average - 18.006 bytes/cycle - 51517.01 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 13.90 cycles/hash
Small key speed test - 2-byte keys - 14.59 cycles/hash
Small key speed test - 3-byte keys - 14.27 cycles/hash
Small key speed test - 4-byte keys - 14.32 cycles/hash
Small key speed test - 5-byte keys - 14.81 cycles/hash
Small key speed test - 6-byte keys - 14.99 cycles/hash
Small key speed test - 7-byte keys - 15.00 cycles/hash
Small key speed test - 8-byte keys - 14.00 cycles/hash
Small key speed test - 9-byte keys - 14.15 cycles/hash
Small key speed test - 10-byte keys - 14.00 cycles/hash
Small key speed test - 11-byte keys - 14.00 cycles/hash
Small key speed test - 12-byte keys - 14.00 cycles/hash
Small key speed test - 13-byte keys - 14.00 cycles/hash
Small key speed test - 14-byte keys - 14.11 cycles/hash
Small key speed test - 15-byte keys - 14.00 cycles/hash
Small key speed test - 16-byte keys - 14.72 cycles/hash
Small key speed test - 17-byte keys - 15.74 cycles/hash
Small key speed test - 18-byte keys - 15.62 cycles/hash
Small key speed test - 19-byte keys - 15.60 cycles/hash
Small key speed test - 20-byte keys - 15.66 cycles/hash
Small key speed test - 21-byte keys - 15.83 cycles/hash
Small key speed test - 22-byte keys - 15.69 cycles/hash
Small key speed test - 23-byte keys - 15.73 cycles/hash
Small key speed test - 24-byte keys - 15.63 cycles/hash
Small key speed test - 25-byte keys - 15.67 cycles/hash
Small key speed test - 26-byte keys - 15.66 cycles/hash
Small key speed test - 27-byte keys - 15.76 cycles/hash
Small key speed test - 28-byte keys - 15.67 cycles/hash
Small key speed test - 29-byte keys - 15.68 cycles/hash
Small key speed test - 30-byte keys - 15.65 cycles/hash
Small key speed test - 31-byte keys - 15.64 cycles/hash
Average 14.971 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 148.866 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 85.492 cycles/op (1.0 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 158.746 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 74.728 cycles/op (2.5 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 62.909 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 32.332 cycles/op (0.2 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.585333%
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.695333%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.619333%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.662667%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.706000%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.693333%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.730667%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.706667%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.672000%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.786667%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.621333%
Testing 160-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.710000%
Testing 512-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.835333%
Testing 1024-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.800000%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 19 bits: 2358/2368 (1.00x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 24 bits: 90/76 (1.18x)
Testing distribution - Worst bias is the 13-bit window at bit 41 - 0.600%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 179 (0.95x)
Testing collisions (high 24-35 bits) - Worst is 25 bits: 23889/23794 (1.00x)
Testing collisions (low 32-bit) - Expected 188.2, actual 215 (1.14x) (27)
Testing collisions (low 24-35 bits) - Worst is 33 bits: 108/94 (1.15x)
Testing distribution - Worst bias is the 17-bit window at bit 16 - 0.075%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2390 (1.01x) (18)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 602/593 (1.01x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2313 (0.98x)
Testing collisions (low 25-38 bits) - Worst is 35 bits: 301/296 (1.01x)
Testing distribution - Worst bias is the 19-bit window at bit 15 - 0.051%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2509 (1.02x) (49)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 42/38 (1.09x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2449 (1.00x) (-11)
Testing collisions (low 25-38 bits) - Worst is 35 bits: 316/307 (1.03x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.058%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23532 (1.00x) (95)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 28/22 (1.22x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23333 (1.00x) (-104)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 96/91 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 16 - 0.020%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2026 (0.98x)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 518/517 (1.00x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2139 (1.03x) (71)
Testing collisions (low 25-38 bits) - Worst is 36 bits: 142/129 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 8 - 0.043%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8072 (1.01x) (51)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 37/31 (1.18x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8035 (1.00x) (14)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 32/31 (1.02x)
Testing distribution - Worst bias is the 20-bit window at bit 37 - 0.039%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26268 (0.99x) (-183)
Testing collisions (high 27-42 bits) - Worst is 39 bits: 225/206 (1.09x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26552 (1.00x) (101)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3381/3309 (1.02x)
Testing distribution - Worst bias is the 20-bit window at bit 36 - 0.027%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1392 (0.99x) (-8)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 93/87 (1.06x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1402 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 48/43 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 11 - 0.051%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84403 (1.00x) (-143)
Testing collisions (high 28-44 bits) - Worst is 34 bits: 21303/21169 (1.01x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84679 (1.00x) (133)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 45/41 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.014%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 930 (1.02x) (20)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 71/56 (1.25x)
Testing collisions (low 32-bit) - Expected 910.2, actual 922 (1.01x) (12)
Testing collisions (low 25-37 bits) - Worst is 35 bits: 121/113 (1.06x)
Testing distribution - Worst bias is the 19-bit window at bit 52 - 0.094%
Keyset 'Sparse' - 512-bit keys with up to 3 bits set - 22370049 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 58155.4, actual 58001 (1.00x) (-154)
Testing collisions (high 28-43 bits) - Worst is 42 bits: 69/56 (1.21x)
Testing collisions (low 32-bit) - Expected 58155.4, actual 58151 (1.00x) (-4)
Testing collisions (low 28-43 bits) - Worst is 41 bits: 121/113 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.015%
Keyset 'Sparse' - 1024-bit keys with up to 2 bits set - 524801 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32.1, actual 37 (1.15x) (5)
Testing collisions (high 22-32 bits) - Worst is 31 bits: 75/64 (1.17x)
Testing collisions (low 32-bit) - Expected 32.1, actual 40 (1.25x) (8)
Testing collisions (low 22-32 bits) - Worst is 32 bits: 40/32 (1.25x)
Testing distribution - Worst bias is the 16-bit window at bit 17 - 0.136%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 512.4, actual 551 (1.08x) (39)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1105/1024 (1.08x)
Testing collisions (low 32-bit) - Expected 512.4, actual 526 (1.03x) (14)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/64 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.103%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 670 (1.00x) (2)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 28/20 (1.34x)
Testing collisions (low 32-bit) - Expected 668.6, actual 675 (1.01x) (7)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 350/334 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 1 - 0.086%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 620 (0.93x)
Testing collisions (high 24-37 bits) - Worst is 36 bits: 45/41 (1.08x)
Testing collisions (low 32-bit) - Expected 668.6, actual 634 (0.95x)
Testing collisions (low 24-37 bits) - Worst is 28 bits: 10778/10667 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.053%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17547 (1.01x) (225)
Testing collisions (high 27-41 bits) - Worst is 38 bits: 283/270 (1.04x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17096 (0.99x) (-226)
Testing collisions (low 27-41 bits) - Worst is 41 bits: 37/33 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.035%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8170 (1.00x) (-16)
Testing collisions (high 26-40 bits) - Worst is 33 bits: 4129/4094 (1.01x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8208 (1.00x) (22)
Testing collisions (low 26-40 bits) - Worst is 33 bits: 4128/4094 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 21 - 0.041%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8179 (1.00x) (-7)
Testing collisions (high 26-40 bits) - Worst is 31 bits: 16584/16362 (1.01x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8243 (1.01x) (57)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.042%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8296 (1.01x) (110)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 74/63 (1.16x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8168 (1.00x) (-18)
Testing collisions (low 26-40 bits) - Worst is 38 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.035%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8186 (1.00x)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 72/63 (1.13x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8238 (1.01x) (52)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1063/1023 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 63 - 0.031%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8212 (1.00x) (26)
Testing collisions (high 26-40 bits) - Worst is 34 bits: 2056/2047 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8083 (0.99x) (-103)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 259/255 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.037%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8052 (0.98x) (-134)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8267 (1.01x) (81)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 2 - 0.045%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8136 (0.99x) (-50)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 130/127 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8138 (0.99x) (-48)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 58 - 0.044%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 7961 (0.97x)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8088 (0.99x) (-98)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1038/1023 (1.01x)
Testing distribution - Worst bias is the 20-bit window at bit 24 - 0.047%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8054 (0.98x) (-132)
Testing collisions (high 26-40 bits) - Worst is 26 bits: 502359/503108 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8309 (1.01x) (123)
Testing collisions (low 26-40 bits) - Worst is 34 bits: 2101/2047 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 14 - 0.052%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8247 (1.01x) (61)
Testing collisions (high 26-40 bits) - Worst is 33 bits: 4159/4094 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8217 (1.00x) (31)
Testing collisions (low 26-40 bits) - Worst is 39 bits: 71/63 (1.11x)
Testing distribution - Worst bias is the 20-bit window at bit 31 - 0.052%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8076 (0.99x) (-110)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 150/127 (1.17x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8278 (1.01x) (92)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1069/1023 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 36 - 0.041%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8108 (0.99x) (-78)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8100 (0.99x) (-86)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 295/255 (1.15x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.049%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 30 bits: 499/465 (1.07x)
Testing collisions (low 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (low 23-34 bits) - Worst is 25 bits: 14836/14754 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 24 - 0.118%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 96 (0.82x)
Testing collisions (high 23-34 bits) - Worst is 26 bits: 7398/7413 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (low 23-34 bits) - Worst is 25 bits: 14917/14754 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 46 - 0.104%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (high 23-34 bits) - Worst is 29 bits: 934/930 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 35/29 (1.20x)
Testing distribution - Worst bias is the 17-bit window at bit 39 - 0.070%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 112 (0.96x)
Testing collisions (high 23-34 bits) - Worst is 27 bits: 3790/3716 (1.02x)
Testing collisions (low 32-bit) - Expected 116.4, actual 140 (1.20x) (24)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 39 - 0.112%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 30 bits: 483/465 (1.04x)
Testing distribution - Worst bias is the 17-bit window at bit 52 - 0.132%
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 132 (1.13x) (16)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing collisions (low 32-bit) - Expected 116.4, actual 100 (0.86x)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 947/930 (1.02x)
Testing distribution - Worst bias is the 17-bit window at bit 15 - 0.109%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 38 (0.77x)
Testing collisions (high 23-33 bits) - Worst is 23 bits: 24681/24735 (1.00x)
Testing collisions (low 32-bit) - Expected 49.6, actual 44 (0.89x)
Testing collisions (low 23-33 bits) - Worst is 31 bits: 108/99 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 22 - 0.120%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3447 (0.99x) (-36)
Testing collisions (high 26-39 bits) - Worst is 26 bits: 217954/217072 (1.00x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3344 (0.96x)
Testing collisions (low 26-39 bits) - Worst is 38 bits: 57/54 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 10 - 0.087%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 40269 (1.00x) (-20)
Testing collisions (high 27-42 bits) - Worst is 38 bits: 644/630 (1.02x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40110 (1.00x) (-179)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 41/39 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.024%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 227182.3, actual 227638 (1.00x) (456)
Testing collisions (high 29-45 bits) - Worst is 39 bits: 1794/1780 (1.01x)
Testing collisions (low 32-bit) - Expected 227182.3, actual 226966 (1.00x) (-216)
Testing collisions (low 29-45 bits) - Worst is 42 bits: 253/222 (1.14x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.009%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 865959.1, actual 866176 (1.00x) (217)
Testing collisions (high 30-47 bits) - Worst is 43 bits: 437/425 (1.03x)
Testing collisions (low 32-bit) - Expected 865959.1, actual 865959 (1.00x)
Testing collisions (low 30-47 bits) - Worst is 45 bits: 121/106 (1.14x)
Testing distribution - Worst bias is the 20-bit window at bit 45 - 0.004%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25652 (1.01x) (263)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 31/24 (1.25x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25380 (1.00x) (-9)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 412/397 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 12 - 0.022%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25507 (1.00x) (118)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 29/24 (1.17x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25496 (1.00x) (107)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 204/198 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 61 - 0.021%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25559 (1.01x) (170)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 60/49 (1.21x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25471 (1.00x) (82)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 35/24 (1.41x)
Testing distribution - Worst bias is the 20-bit window at bit 63 - 0.033%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1822 (0.98x)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 30/29 (1.03x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1837 (0.99x) (-25)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 229258/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 42 - 0.071%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1827 (0.98x) (-35)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 131/116 (1.13x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1832 (0.98x) (-30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 32/29 (1.10x)
Testing distribution - Worst bias is the 19-bit window at bit 24 - 0.069%
Keyset 'Words' - 466569 dict words
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 27 (1.07x) (2)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 27/25 (1.07x)
Testing collisions (low 32-bit) - Expected 25.3, actual 26 (1.03x) (1)
Testing collisions (low 22-32 bits) - Worst is 30 bits: 120/101 (1.18x)
Testing distribution - Worst bias is the 16-bit window at bit 48 - 0.155%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (high 21-29 bits) - Worst is 28 bits: 83/78 (1.06x)
Testing collisions (low 32-bit) - Expected 4.9, actual 2 (0.41x)
Testing collisions (low 21-29 bits) - Worst is 27 bits: 169/156 (1.08x)
Testing distribution - Worst bias is the 15-bit window at bit 31 - 0.261%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2808 (0.97x)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 93/90 (1.02x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 3032 (1.04x) (123)
Testing collisions (low 26-39 bits) - Worst is 38 bits: 64/45 (1.41x)
Testing distribution - Worst bias is the 19-bit window at bit 63 - 0.044%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32777 (1.00x) (52)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32511 (0.99x) (-214)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 36/31 (1.13x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1087 (0.97x)
Testing collisions (high 25-37 bits) - Worst is 27 bits: 35400/35452 (1.00x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1067 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 34 bits: 280/279 (1.00x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 3 - 0.118%
Testing bit 1
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64220/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 60 - 0.077%
Testing bit 2
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.078%
Testing bit 3
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1083/1023 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 470 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2093/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.069%
Testing bit 4
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8315/8170 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.081%
Testing bit 5
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 40 - 0.116%
Testing bit 6
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 14 - 0.106%
Testing bit 7
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.095%
Testing bit 8
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 513/511 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.075%
Testing bit 9
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.113%
Testing bit 10
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32540/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 40 - 0.099%
Testing bit 11
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32534/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.059%
Testing bit 12
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 46/31 (1.44x)
Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.058%
Testing bit 13
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8316/8170 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.054%
Testing bit 14
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 43 - 0.088%
Testing bit 15
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 281/255 (1.10x)
Testing collisions (low 32-bit) - Expected 511.9, actual 555 (1.08x) (44)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.079%
Testing bit 16
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2084/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 520/511 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 58 - 0.076%
Testing bit 17
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing distribution - Worst bias is the 17-bit window at bit 54 - 0.068%
Testing bit 18
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2114/2046 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.083%
Testing bit 19
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 27 bits: 16419/16298 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.080%
Testing bit 20
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1040/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.092%
Testing bit 21
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1062/1023 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 34 - 0.079%
Testing bit 22
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 466 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 47 - 0.077%
Testing bit 23
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 26 bits: 32370/32429 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.064%
Testing bit 24
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 272/255 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 27 bits: 16412/16298 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.057%
Testing bit 25
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 549/511 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.089%
Testing bit 26
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1036/1023 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 481 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 1 - 0.065%
Testing bit 27
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing collisions (low 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 13 - 0.101%
Testing bit 28
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64180/64191 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 536 (1.05x) (25)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.130%
Testing bit 29
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126329/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1036/1023 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 3 - 0.059%
Testing bit 30
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2107/2046 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 478 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64338/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.078%
Testing bit 31
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125635/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.072%
Testing bit 32
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125444/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4149/4090 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.094%
Testing bit 33
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 546/511 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 45 - 0.069%
Testing bit 34
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.133%
Testing bit 35
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 31 - 0.062%
Testing bit 36
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2121/2046 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.119%
Testing bit 37
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 77/63 (1.20x)
Testing collisions (low 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4123/4090 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.082%
Testing bit 38
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 542 (1.06x) (31)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1065/1023 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.058%
Testing bit 39
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 523 (1.02x) (12)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1056/1023 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 479 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125933/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 11 - 0.066%
Testing bit 40
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 47/31 (1.47x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 134/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.109%
Testing bit 41
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 474 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16336/16298 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1038/1023 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.067%
Testing bit 42
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 137/127 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.079%
Testing bit 43
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 38 - 0.089%
Testing bit 44
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing collisions (low 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.073%
Testing bit 45
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 47 - 0.092%
Testing bit 46
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 143/127 (1.12x)
Testing collisions (low 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (low 24-36 bits) - Worst is 29 bits: 4172/4090 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.073%
Testing bit 47
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 263/255 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 560 (1.09x) (49)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 24 - 0.073%
Testing bit 48
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4230/4090 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.069%
Testing bit 49
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 265/255 (1.04x)
Testing collisions (low 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.077%
Testing bit 50
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.088%
Testing bit 51
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 461 (0.90x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2065/2046 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2045/2046 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 2 - 0.073%
Testing bit 52
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 527/511 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.083%
Testing bit 53
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 126008/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 18 - 0.083%
Testing bit 54
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 544/511 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.052%
Testing bit 55
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 454 (0.89x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125990/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8220/8170 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 21 - 0.108%
Testing bit 56
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 561 (1.10x) (50)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 148/127 (1.16x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.067%
Testing bit 57
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 468 (0.91x)
Testing collisions (high 24-36 bits) - Worst is 26 bits: 32528/32429 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.111%
Testing bit 58
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 48/31 (1.50x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 264/255 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.080%
Testing bit 59
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2078/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2113/2046 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.088%
Testing bit 60
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 465 (0.91x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8183/8170 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 38 - 0.067%
Testing bit 61
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.075%
Testing bit 62
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 572 (1.12x) (61)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 304/255 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.068%
Testing bit 63
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 529/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.089%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38919570.005124 - 273658.703091
Popcount 0 stats : 38918319.622388 - 273636.624134
MomentChi2 for bits 1 : 3.42946
MomentChi2 for bits 0 : 0.0261471
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919286.374871 - 273651.807467
Popcount 0 stats : 38918700.222160 - 273641.544934
MomentChi2 for deriv b1 : 2.15648
MomentChi2 for deriv b0 : 0.457215
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 130706 (1.00x) (-25)
Testing collisions (high 28-44 bits) - Worst is 44 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 130394 (1.00x) (-337)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 261/255 (1.02x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 681.305909 seconds
------------------------------------------------------------
```
------------------------------------------------------------
### Stable Hash 64bit SMHasher Results
The following results were achieved on my personal computer when testing the 64bit variant of the facil.io Stable Hash (`fio_stable_hash`).
```txt
------------------------------------------------------------
--- Testing Stable "facil.io Stable Hash 64bit" GOOD
[[[ Sanity Tests ]]]
Verification value 0x6993BB92 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 12.548 bytes/cycle - 35899.15 MiB/sec @ 3 ghz
Alignment 6 - 12.560 bytes/cycle - 35934.05 MiB/sec @ 3 ghz
Alignment 5 - 12.554 bytes/cycle - 35917.07 MiB/sec @ 3 ghz
Alignment 4 - 12.560 bytes/cycle - 35935.44 MiB/sec @ 3 ghz
Alignment 3 - 12.559 bytes/cycle - 35931.79 MiB/sec @ 3 ghz
Alignment 2 - 12.557 bytes/cycle - 35926.78 MiB/sec @ 3 ghz
Alignment 1 - 12.561 bytes/cycle - 35936.39 MiB/sec @ 3 ghz
Alignment 0 - 12.678 bytes/cycle - 36273.41 MiB/sec @ 3 ghz
Average - 12.572 bytes/cycle - 35969.26 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 20.57 cycles/hash
Small key speed test - 2-byte keys - 21.00 cycles/hash
Small key speed test - 3-byte keys - 21.00 cycles/hash
Small key speed test - 4-byte keys - 20.99 cycles/hash
Small key speed test - 5-byte keys - 21.00 cycles/hash
Small key speed test - 6-byte keys - 21.00 cycles/hash
Small key speed test - 7-byte keys - 22.00 cycles/hash
Small key speed test - 8-byte keys - 21.00 cycles/hash
Small key speed test - 9-byte keys - 21.00 cycles/hash
Small key speed test - 10-byte keys - 21.00 cycles/hash
Small key speed test - 11-byte keys - 21.00 cycles/hash
Small key speed test - 12-byte keys - 21.00 cycles/hash
Small key speed test - 13-byte keys - 21.00 cycles/hash
Small key speed test - 14-byte keys - 21.00 cycles/hash
Small key speed test - 15-byte keys - 21.00 cycles/hash
Small key speed test - 16-byte keys - 21.00 cycles/hash
Small key speed test - 17-byte keys - 22.88 cycles/hash
Small key speed test - 18-byte keys - 22.92 cycles/hash
Small key speed test - 19-byte keys - 22.94 cycles/hash
Small key speed test - 20-byte keys - 22.93 cycles/hash
Small key speed test - 21-byte keys - 22.94 cycles/hash
Small key speed test - 22-byte keys - 22.92 cycles/hash
Small key speed test - 23-byte keys - 22.89 cycles/hash
Small key speed test - 24-byte keys - 22.89 cycles/hash
Small key speed test - 25-byte keys - 22.93 cycles/hash
Small key speed test - 26-byte keys - 22.90 cycles/hash
Small key speed test - 27-byte keys - 23.03 cycles/hash
Small key speed test - 28-byte keys - 22.81 cycles/hash
Small key speed test - 29-byte keys - 22.89 cycles/hash
Small key speed test - 30-byte keys - 23.13 cycles/hash
Small key speed test - 31-byte keys - 22.99 cycles/hash
Average 21.953 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 155.249 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 93.391 cycles/op (1.6 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 143.332 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 93.590 cycles/op (1.3 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 87.173 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 49.115 cycles/op (0.5 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.632667%
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.704000%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.696667%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.686000%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.636000%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.630667%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.690667%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.670667%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.760667%
Testing 160-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.773333%
Testing 512-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.825333%
Testing 1024-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.753333%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 23 bits: 171/152 (1.12x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 21 bits: 602/606 (0.99x)
Testing distribution - Worst bias is the 13-bit window at bit 31 - 0.586%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 197 (1.05x) (9)
Testing collisions (high 24-35 bits) - Worst is 33 bits: 100/94 (1.06x)
Testing collisions (low 32-bit) - Expected 188.2, actual 170 (0.90x)
Testing collisions (low 24-35 bits) - Worst is 26 bits: 12071/11972 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 48 - 0.087%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2345 (0.99x) (-27)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 47/37 (1.27x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2374 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 30 bits: 9492/9478 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 35 - 0.057%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2380 (0.97x)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 163/153 (1.06x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2490 (1.01x) (30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 45/38 (1.17x)
Testing distribution - Worst bias is the 19-bit window at bit 50 - 0.061%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23277 (0.99x) (-160)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 28/22 (1.22x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23272 (0.99x) (-165)
Testing collisions (low 27-42 bits) - Worst is 30 bits: 93648/93442 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 29 - 0.022%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2065 (1.00x) (-3)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 37/32 (1.14x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2042 (0.99x) (-26)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 40/32 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 37 - 0.050%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8020 (1.00x) (-1)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 36/31 (1.15x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8038 (1.00x) (17)
Testing collisions (low 26-40 bits) - Worst is 31 bits: 16101/16033 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.043%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26679 (1.01x) (228)
Testing collisions (high 27-42 bits) - Worst is 39 bits: 218/206 (1.05x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26434 (1.00x) (-17)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3324/3309 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 26 - 0.028%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1462 (1.04x) (62)
Testing collisions (high 25-38 bits) - Worst is 37 bits: 53/43 (1.21x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1406 (1.00x) (6)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 60/43 (1.37x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.088%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84527 (1.00x) (-19)
Testing collisions (high 28-44 bits) - Worst is 44 bits: 29/20 (1.40x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84586 (1.00x) (40)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 172/165 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 39 - 0.017%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 947 (1.04x) (37)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 63/56 (1.11x)
Testing collisions (low 32-bit) - Expected 910.2, actual 925 (1.02x) (15)
Testing collisions (low 25-37 bits) - Worst is 33 bits: 473/455 (1.04x)
Testing distribution - Worst bias is the 19-bit window at bit 50 - 0.082%
Keyset 'Sparse' - 512-bit keys with up to 3 bits set - 22370049 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 58155.4, actual 57664 (0.99x) (-491)
Testing collisions (high 28-43 bits) - Worst is 38 bits: 924/910 (1.02x)
Testing collisions (low 32-bit) - Expected 58155.4, actual 58193 (1.00x) (38)
Testing collisions (low 28-43 bits) - Worst is 39 bits: 498/455 (1.09x)
Testing distribution - Worst bias is the 20-bit window at bit 23 - 0.017%
Keyset 'Sparse' - 1024-bit keys with up to 2 bits set - 524801 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32.1, actual 30 (0.94x)
Testing collisions (high 22-32 bits) - Worst is 28 bits: 518/512 (1.01x)
Testing collisions (low 32-bit) - Expected 32.1, actual 23 (0.72x)
Testing collisions (low 22-32 bits) - Worst is 29 bits: 294/256 (1.15x)
Testing distribution - Worst bias is the 16-bit window at bit 48 - 0.155%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 512.4, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4139/4094 (1.01x)
Testing collisions (low 32-bit) - Expected 512.4, actual 529 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 143/128 (1.12x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.057%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 670 (1.00x) (2)
Testing collisions (high 24-37 bits) - Worst is 36 bits: 44/41 (1.05x)
Testing collisions (low 32-bit) - Expected 668.6, actual 655 (0.98x)
Testing collisions (low 24-37 bits) - Worst is 34 bits: 173/167 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.080%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 657 (0.98x) (-11)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 24/20 (1.15x)
Testing collisions (low 32-bit) - Expected 668.6, actual 691 (1.03x) (23)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 362/334 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.047%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17106 (0.99x) (-216)
Testing collisions (high 27-41 bits) - Worst is 41 bits: 37/33 (1.09x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17214 (0.99x) (-108)
Testing collisions (low 27-41 bits) - Worst is 28 bits: 273805/273271 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.030%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8288 (1.01x) (102)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8135 (0.99x) (-51)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 52 - 0.038%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8207 (1.00x) (21)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8100 (0.99x) (-86)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.036%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 7983 (0.98x)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 34/31 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8152 (1.00x) (-34)
Testing collisions (low 26-40 bits) - Worst is 36 bits: 525/511 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 4 - 0.054%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8201 (1.00x) (15)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8064 (0.99x) (-122)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 49 - 0.040%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8156 (1.00x) (-30)
Testing collisions (high 26-40 bits) - Worst is 28 bits: 130239/129717 (1.00x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8355 (1.02x) (169)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 20-bit window at bit 44 - 0.040%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8046 (0.98x) (-140)
Testing collisions (high 26-40 bits) - Worst is 36 bits: 550/511 (1.07x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8034 (0.98x) (-152)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 265/255 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 24 - 0.042%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8155 (1.00x) (-31)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 84/63 (1.31x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8295 (1.01x) (109)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 20-bit window at bit 4 - 0.036%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8248 (1.01x) (62)
Testing collisions (high 26-40 bits) - Worst is 37 bits: 279/255 (1.09x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8246 (1.01x) (60)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 20-bit window at bit 51 - 0.040%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8174 (1.00x) (-12)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 136/127 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8053 (0.98x) (-133)
Testing collisions (low 26-40 bits) - Worst is 37 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.043%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8175 (1.00x) (-11)
Testing collisions (high 26-40 bits) - Worst is 39 bits: 68/63 (1.06x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8116 (0.99x) (-70)
Testing collisions (low 26-40 bits) - Worst is 40 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 20-bit window at bit 10 - 0.042%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8223 (1.00x) (37)
Testing collisions (high 26-40 bits) - Worst is 38 bits: 130/127 (1.02x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8294 (1.01x) (108)
Testing collisions (low 26-40 bits) - Worst is 38 bits: 149/127 (1.16x)
Testing distribution - Worst bias is the 20-bit window at bit 55 - 0.045%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8186.7, actual 8163 (1.00x) (-23)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 8186.7, actual 8198 (1.00x) (12)
Testing collisions (low 26-40 bits) - Worst is 35 bits: 1055/1023 (1.03x)
Testing distribution - Worst bias is the 20-bit window at bit 45 - 0.041%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 104 (0.89x)
Testing collisions (high 23-34 bits) - Worst is 24 bits: 29355/29218 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 107 (0.92x)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing distribution - Worst bias is the 17-bit window at bit 20 - 0.156%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 117 (1.01x) (1)
Testing collisions (high 23-34 bits) - Worst is 32 bits: 117/116 (1.01x)
Testing collisions (low 32-bit) - Expected 116.4, actual 111 (0.95x)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 40 - 0.129%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 95 (0.82x)
Testing collisions (high 23-34 bits) - Worst is 24 bits: 29375/29218 (1.01x)
Testing collisions (low 32-bit) - Expected 116.4, actual 136 (1.17x) (20)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 12 - 0.166%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 110 (0.94x)
Testing collisions (high 23-34 bits) - Worst is 23 bits: 57228/57305 (1.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 128 (1.10x) (12)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 34/29 (1.17x)
Testing distribution - Worst bias is the 17-bit window at bit 40 - 0.096%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 102 (0.88x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 30/29 (1.03x)
Testing collisions (low 32-bit) - Expected 116.4, actual 111 (0.95x)
Testing collisions (low 23-34 bits) - Worst is 24 bits: 29234/29218 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 27 - 0.099%
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 128 (1.10x) (12)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 930/930 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 30 - 0.148%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (high 23-33 bits) - Worst is 33 bits: 29/24 (1.17x)
Testing collisions (low 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (low 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 16 - 0.189%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3504 (1.01x) (21)
Testing collisions (high 26-39 bits) - Worst is 39 bits: 31/27 (1.14x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3493 (1.00x) (10)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 38/27 (1.40x)
Testing distribution - Worst bias is the 20-bit window at bit 52 - 0.096%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 40041 (0.99x) (-248)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 45/39 (1.14x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40866 (1.01x) (577)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 177/157 (1.12x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.021%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 227182.3, actual 227276 (1.00x) (94)
Testing collisions (high 29-45 bits) - Worst is 40 bits: 938/890 (1.05x)
Testing collisions (low 32-bit) - Expected 227182.3, actual 227236 (1.00x) (54)
Testing collisions (low 29-45 bits) - Worst is 45 bits: 37/27 (1.33x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.006%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 865959.1, actual 866307 (1.00x) (348)
Testing collisions (high 30-47 bits) - Worst is 37 bits: 27622/27237 (1.01x)
Testing collisions (low 32-bit) - Expected 865959.1, actual 865094 (1.00x) (-865)
Testing collisions (low 30-47 bits) - Worst is 46 bits: 68/53 (1.28x)
Testing distribution - Worst bias is the 20-bit window at bit 37 - 0.004%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25376 (1.00x) (-13)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 26/24 (1.05x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25263 (1.00x) (-126)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 425/397 (1.07x)
Testing distribution - Worst bias is the 19-bit window at bit 27 - 0.018%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25364 (1.00x) (-25)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 27/24 (1.09x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25570 (1.01x) (181)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 30/24 (1.21x)
Testing distribution - Worst bias is the 20-bit window at bit 13 - 0.031%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25556 (1.01x) (167)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 63/49 (1.27x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25622 (1.01x) (233)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 212/198 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 54 - 0.031%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1859 (1.00x) (-3)
Testing collisions (high 25-38 bits) - Worst is 37 bits: 70/58 (1.20x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1926 (1.03x) (64)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 30 - 0.067%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1860 (1.00x) (-2)
Testing collisions (high 25-38 bits) - Worst is 27 bits: 59197/59016 (1.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1829 (0.98x) (-33)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 228540/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 7 - 0.042%
Keyset 'Words' - 466569 dict words
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 31 (1.22x) (6)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 31/25 (1.22x)
Testing collisions (low 32-bit) - Expected 25.3, actual 20 (0.79x)
Testing collisions (low 22-32 bits) - Worst is 26 bits: 1650/1618 (1.02x)
Testing distribution - Worst bias is the 16-bit window at bit 1 - 0.220%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 5 (1.02x) (1)
Testing collisions (high 21-29 bits) - Worst is 29 bits: 44/39 (1.13x)
Testing collisions (low 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (low 21-29 bits) - Worst is 29 bits: 49/39 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 29 - 0.313%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2994 (1.03x) (85)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 103/90 (1.13x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 2973 (1.02x) (64)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 36/22 (1.58x)
Testing distribution - Worst bias is the 19-bit window at bit 10 - 0.042%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32957 (1.01x) (232)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32857 (1.00x) (132)
Testing collisions (low 27-42 bits) - Worst is 41 bits: 69/63 (1.08x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1110 (0.99x) (-6)
Testing collisions (high 25-37 bits) - Worst is 31 bits: 2271/2231 (1.02x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1068 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 37 bits: 42/34 (1.20x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 473 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 145/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.095%
Testing bit 1
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing collisions (low 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 17-bit window at bit 23 - 0.077%
Testing bit 2
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 479 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8185/8170 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.062%
Testing bit 3
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 490 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2047/2046 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8326/8170 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.061%
Testing bit 4
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 463 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64159/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.076%
Testing bit 5
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 58 - 0.071%
Testing bit 6
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 17-bit window at bit 28 - 0.054%
Testing bit 7
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 553 (1.08x) (42)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 278/255 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 17-bit window at bit 60 - 0.058%
Testing bit 8
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.088%
Testing bit 9
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.063%
Testing bit 10
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 131/127 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.081%
Testing bit 11
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 67/63 (1.05x)
Testing distribution - Worst bias is the 17-bit window at bit 45 - 0.064%
Testing bit 12
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.086%
Testing bit 13
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 517/511 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 471 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125649/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.071%
Testing bit 14
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.104%
Testing bit 15
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 513/511 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2182/2046 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.062%
Testing bit 16
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.103%
Testing bit 17
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64414/64191 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.090%
Testing bit 18
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.072%
Testing bit 19
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 45 - 0.055%
Testing bit 20
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 283/255 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 548 (1.07x) (37)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.061%
Testing bit 21
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 489 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4170/4090 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.063%
Testing bit 22
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.111%
Testing bit 23
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 486 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8215/8170 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.096%
Testing bit 24
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.085%
Testing bit 25
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 2 - 0.072%
Testing bit 26
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1046/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 44 - 0.060%
Testing bit 27
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.067%
Testing bit 28
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1027/1023 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 44 - 0.072%
Testing bit 29
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 560 (1.09x) (49)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 153/127 (1.20x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 17-bit window at bit 59 - 0.067%
Testing bit 30
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 484 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4102/4090 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 516/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.084%
Testing bit 31
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 549 (1.07x) (38)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 549/511 (1.07x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.070%
Testing bit 32
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.086%
Testing bit 33
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 536 (1.05x) (25)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 568 (1.11x) (57)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.075%
Testing bit 34
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 257/255 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 24 - 0.047%
Testing bit 35
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 28 - 0.061%
Testing bit 36
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 43/31 (1.34x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2062/2046 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.092%
Testing bit 37
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.085%
Testing bit 38
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 489 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 556/511 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.045%
Testing bit 39
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 282/255 (1.10x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.081%
Testing bit 40
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing collisions (low 32-bit) - Expected 511.9, actual 459 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.095%
Testing bit 41
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.100%
Testing bit 42
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1041/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.080%
Testing bit 43
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 44
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2095/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.088%
Testing bit 45
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 559 (1.09x) (48)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.073%
Testing bit 46
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.073%
Testing bit 47
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 472 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125700/125777 (1.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 11 - 0.084%
Testing bit 48
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 26 bits: 32601/32429 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.053%
Testing bit 49
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1046/1023 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.074%
Testing bit 50
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 56 - 0.061%
Testing bit 51
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.079%
Testing bit 52
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8213/8170 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 28 - 0.110%
Testing bit 53
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.073%
Testing bit 54
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 283/255 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 5 - 0.079%
Testing bit 55
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.066%
Testing bit 56
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4143/4090 (1.01x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 18-bit window at bit 17 - 0.112%
Testing bit 57
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.142%
Testing bit 58
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 496 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 131/127 (1.02x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 59
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 541 (1.06x) (30)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.056%
Testing bit 60
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 82/63 (1.28x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.083%
Testing bit 61
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 545 (1.06x) (34)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 545/511 (1.06x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.083%
Testing bit 62
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.095%
Testing bit 63
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 264/255 (1.03x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.077%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38918998.920034 - 273638.558723
Popcount 0 stats : 38918770.531038 - 273645.996303
MomentChi2 for bits 1 : 1.16628
MomentChi2 for bits 0 : 0.594771
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919257.619283 - 273628.012809
Popcount 0 stats : 38918472.697479 - 273650.071385
MomentChi2 for deriv b1 : 2.04392
MomentChi2 for deriv b0 : 0.135878
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 130884 (1.00x) (153)
Testing collisions (high 28-44 bits) - Worst is 43 bits: 75/63 (1.17x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 131211 (1.00x) (480)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 68/63 (1.06x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 755.424002 seconds
------------------------------------------------------------
```
------------------------------------------------------------
### Stable Hash 128bit SMHasher Results
The following results were achieved on my personal computer when testing the 128bit variant of the facil.io Stable Hash (`fio_stable_hash128`).
```txt
------------------------------------------------------------
--- Testing Stable128 "facil.io Stable Hash 128bit" GOOD
[[[ Sanity Tests ]]]
Verification value 0x97D1AB52 ....... PASS
Running sanity check 1 .......... PASS
Running AppendedZeroesTest .......... PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 12.633 bytes/cycle - 36143.93 MiB/sec @ 3 ghz
Alignment 6 - 12.651 bytes/cycle - 36193.49 MiB/sec @ 3 ghz
Alignment 5 - 12.646 bytes/cycle - 36179.39 MiB/sec @ 3 ghz
Alignment 4 - 12.644 bytes/cycle - 36174.76 MiB/sec @ 3 ghz
Alignment 3 - 12.656 bytes/cycle - 36208.15 MiB/sec @ 3 ghz
Alignment 2 - 12.650 bytes/cycle - 36192.29 MiB/sec @ 3 ghz
Alignment 1 - 12.644 bytes/cycle - 36175.29 MiB/sec @ 3 ghz
Alignment 0 - 12.797 bytes/cycle - 36612.89 MiB/sec @ 3 ghz
Average - 12.665 bytes/cycle - 36235.02 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 21.00 cycles/hash
Small key speed test - 2-byte keys - 21.81 cycles/hash
Small key speed test - 3-byte keys - 22.97 cycles/hash
Small key speed test - 4-byte keys - 21.99 cycles/hash
Small key speed test - 5-byte keys - 22.69 cycles/hash
Small key speed test - 6-byte keys - 22.00 cycles/hash
Small key speed test - 7-byte keys - 23.92 cycles/hash
Small key speed test - 8-byte keys - 21.98 cycles/hash
Small key speed test - 9-byte keys - 21.99 cycles/hash
Small key speed test - 10-byte keys - 21.99 cycles/hash
Small key speed test - 11-byte keys - 21.99 cycles/hash
Small key speed test - 12-byte keys - 21.99 cycles/hash
Small key speed test - 13-byte keys - 21.99 cycles/hash
Small key speed test - 14-byte keys - 21.99 cycles/hash
Small key speed test - 15-byte keys - 21.99 cycles/hash
Small key speed test - 16-byte keys - 22.00 cycles/hash
Small key speed test - 17-byte keys - 23.98 cycles/hash
Small key speed test - 18-byte keys - 23.98 cycles/hash
Small key speed test - 19-byte keys - 23.98 cycles/hash
Small key speed test - 20-byte keys - 23.98 cycles/hash
Small key speed test - 21-byte keys - 23.97 cycles/hash
Small key speed test - 22-byte keys - 23.98 cycles/hash
Small key speed test - 23-byte keys - 23.98 cycles/hash
Small key speed test - 24-byte keys - 23.97 cycles/hash
Small key speed test - 25-byte keys - 23.97 cycles/hash
Small key speed test - 26-byte keys - 23.97 cycles/hash
Small key speed test - 27-byte keys - 23.97 cycles/hash
Small key speed test - 28-byte keys - 23.97 cycles/hash
Small key speed test - 29-byte keys - 23.97 cycles/hash
Small key speed test - 30-byte keys - 23.98 cycles/hash
Small key speed test - 31-byte keys - 24.18 cycles/hash
Average 23.037 cycles/hash
[[[ 'Hashmap' Speed Tests ]]]
std::unordered_map
Init std HashMapTest: 151.297 cycles/op (466569 inserts, 1% deletions)
Running std HashMapTest: 93.079 cycles/op (1.2 stdv, found 461903)
greg7mdp/parallel-hashmap
Init fast HashMapTest: 145.520 cycles/op (466569 inserts, 1% deletions)
Running fast HashMapTest: 91.867 cycles/op (0.7 stdv, found 461903)
facil.io HashMap
Init fast fio_map_s Test: 84.812 cycles/op (466569 inserts, 1% deletions)
Running fast fio_map_s Test: 48.436 cycles/op (0.4 stdv, found 461903)
....... PASS
[[[ Avalanche Tests ]]]
Testing 24-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.659333%
Testing 32-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 40-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.723333%
Testing 48-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.696667%
Testing 56-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.694000%
Testing 64-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.712000%
Testing 72-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.676000%
Testing 80-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.704667%
Testing 96-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.690667%
Testing 112-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.721333%
Testing 128-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.760667%
Testing 160-bit keys -> 128-bit hashes, 300000 reps.......... worst bias is 0.773333%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (high 19-25 bits) - Worst is 23 bits: 161/152 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x)
Testing collisions (low 19-25 bits) - Worst is 21 bits: 602/606 (0.99x)
Testing distribution - Worst bias is the 13-bit window at bit 31 - 0.586%
Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 188.2, actual 190 (1.01x) (2)
Testing collisions (high 24-35 bits) - Worst is 32 bits: 190/188 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 188.2, actual 170 (0.90x)
Testing collisions (low 24-35 bits) - Worst is 26 bits: 12071/11972 (1.01x)
Testing distribution - Worst bias is the 17-bit window at bit 35 - 0.080%
Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2372.2, actual 2355 (0.99x) (-17)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 39/37 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2372.2, actual 2374 (1.00x) (2)
Testing collisions (low 25-38 bits) - Worst is 30 bits: 9492/9478 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 74 - 0.065%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2460.8, actual 2429 (0.99x) (-31)
Testing collisions (high 25-38 bits) - Worst is 29 bits: 19779/19637 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2460.8, actual 2490 (1.01x) (30)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 45/38 (1.17x)
Testing distribution - Worst bias is the 19-bit window at bit 82 - 0.051%
Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 23437.8, actual 23636 (1.01x) (199)
Testing collisions (high 27-42 bits) - Worst is 42 bits: 24/22 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 23437.8, actual 23272 (0.99x) (-165)
Testing collisions (low 27-42 bits) - Worst is 30 bits: 93648/93442 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 74 - 0.028%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2069.0, actual 2045 (0.99x) (-23)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 33/32 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2069.0, actual 2042 (0.99x) (-26)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 40/32 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 96 - 0.061%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8021.7, actual 8104 (1.01x) (83)
Testing collisions (high 26-40 bits) - Worst is 40 bits: 39/31 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8021.7, actual 8038 (1.00x) (17)
Testing collisions (low 26-40 bits) - Worst is 31 bits: 16101/16033 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 43 - 0.043%
Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 26451.8, actual 26433 (1.00x) (-18)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 55/51 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 26451.8, actual 26434 (1.00x) (-17)
Testing collisions (low 27-42 bits) - Worst is 35 bits: 3324/3309 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 26 - 0.028%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1401.0, actual 1326 (0.95x)
Testing collisions (high 25-38 bits) - Worst is 38 bits: 22/21 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1401.0, actual 1406 (1.00x) (6)
Testing collisions (low 25-38 bits) - Worst is 37 bits: 60/43 (1.37x)
Testing distribution - Worst bias is the 19-bit window at bit 12 - 0.088%
Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 84546.1, actual 84181 (1.00x) (-365)
Testing collisions (high 28-44 bits) - Worst is 43 bits: 54/41 (1.31x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 84546.1, actual 84586 (1.00x) (40)
Testing collisions (low 28-44 bits) - Worst is 41 bits: 172/165 (1.04x)
Testing distribution - Worst bias is the 20-bit window at bit 39 - 0.017%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 910.2, actual 920 (1.01x) (10)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 65/56 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 910.2, actual 925 (1.02x) (15)
Testing collisions (low 25-37 bits) - Worst is 33 bits: 473/455 (1.04x)
Testing distribution - Worst bias is the 19-bit window at bit 73 - 0.096%
[[[ Keyset 'Permutation' Tests ]]]
Combination Lowbits Tests:
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 735 (1.10x) (67)
Testing collisions (high 24-37 bits) - Worst is 34 bits: 207/167 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 668.6, actual 655 (0.98x)
Testing collisions (low 24-37 bits) - Worst is 34 bits: 173/167 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.080%
Combination Highbits Tests
Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 668.6, actual 681 (1.02x) (13)
Testing collisions (high 24-37 bits) - Worst is 37 bits: 26/20 (1.24x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 668.6, actual 691 (1.03x) (23)
Testing collisions (low 24-37 bits) - Worst is 33 bits: 362/334 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 68 - 0.091%
Combination Hi-Lo Tests:
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 17322.9, actual 17433 (1.01x) (111)
Testing collisions (high 27-41 bits) - Worst is 34 bits: 4416/4333 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 17322.9, actual 17214 (0.99x) (-108)
Testing collisions (low 27-41 bits) - Worst is 28 bits: 273805/273271 (1.00x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.030%
Combination 0x8000000 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 135/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 12 (1.50x) (5)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 15-bit window at bit 48 - 0.268%
Combination 0x0000001 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 26 bits: 537/511 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 73/63 (1.14x)
Testing distribution - Worst bias is the 15-bit window at bit 119 - 0.328%
Combination 0x800000000000000 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 1 (0.13x)
Testing collisions (high 21-30 bits) - Worst is 24 bits: 2103/2037 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 15-bit window at bit 15 - 0.279%
Combination 0x000000000000001 Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 9 (1.13x) (2)
Testing collisions (high 21-30 bits) - Worst is 29 bits: 72/63 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 10 (1.25x) (3)
Testing collisions (low 21-30 bits) - Worst is 24 bits: 2041/2037 (1.00x)
Testing distribution - Worst bias is the 15-bit window at bit 2 - 0.203%
Combination 16-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 3 (0.38x)
Testing collisions (high 21-30 bits) - Worst is 30 bits: 33/31 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 15 (1.88x) (8)
Testing collisions (low 21-30 bits) - Worst is 27 bits: 262/255 (1.02x)
Testing distribution - Worst bias is the 15-bit window at bit 21 - 0.203%
Combination 16-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 132/127 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (low 21-30 bits) - Worst is 25 bits: 1037/1021 (1.02x)
Testing distribution - Worst bias is the 15-bit window at bit 47 - 0.252%
Combination 32-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 13 (1.63x) (6)
Testing collisions (high 21-30 bits) - Worst is 30 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 6 (0.75x)
Testing collisions (low 21-30 bits) - Worst is 25 bits: 1034/1021 (1.01x)
Testing distribution - Worst bias is the 15-bit window at bit 43 - 0.259%
Combination 32-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 5 (0.63x)
Testing collisions (high 21-30 bits) - Worst is 29 bits: 67/63 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 4 (0.50x)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 15-bit window at bit 24 - 0.293%
Combination 64-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (high 21-30 bits) - Worst is 22 bits: 8144/8023 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 3 (0.38x)
Testing collisions (low 21-30 bits) - Worst is 28 bits: 151/127 (1.18x)
Testing distribution - Worst bias is the 15-bit window at bit 74 - 0.329%
Combination 64-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 8 (1.00x) (1)
Testing collisions (high 21-30 bits) - Worst is 27 bits: 286/255 (1.12x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 4 (0.50x)
Testing collisions (low 21-30 bits) - Worst is 24 bits: 2098/2037 (1.03x)
Testing distribution - Worst bias is the 15-bit window at bit 118 - 0.211%
Combination 128-bytes [0-1] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (high 21-30 bits) - Worst is 26 bits: 553/511 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 7 (0.88x)
Testing collisions (low 21-30 bits) - Worst is 29 bits: 78/63 (1.22x)
Testing distribution - Worst bias is the 15-bit window at bit 57 - 0.323%
Combination 128-bytes [0-last] Tests:
Keyset 'Combination' - up to 17 blocks from a set of 2 - 262142 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 8.0, actual 11 (1.38x) (4)
Testing collisions (high 21-30 bits) - Worst is 28 bits: 136/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 8.0, actual 13 (1.63x) (6)
Testing collisions (low 21-30 bits) - Worst is 30 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 125 - 0.223%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test
Window at 0 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 1 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 2 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 3 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 4 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 5 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 6 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 7 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 8 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 9 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 10 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 11 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 12 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 13 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 14 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 15 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 16 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 17 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 18 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 19 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 20 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 21 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 22 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 23 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 24 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 25 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 26 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 27 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 28 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 29 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 30 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 31 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Window at 32 - Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 33 bits: 63/58 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (low 23-34 bits) - Worst is 29 bits: 930/930 (1.00x)
Testing distribution - Worst bias is the 17-bit window at bit 30 - 0.148%
Keyset 'Cyclic' - 8 cycles of 17 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 115 (0.99x) (-1)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1937/1860 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 123 (1.06x) (7)
Testing collisions (low 23-34 bits) - Worst is 31 bits: 264/232 (1.13x)
Testing distribution - Worst bias is the 17-bit window at bit 38 - 0.131%
Keyset 'Cyclic' - 8 cycles of 18 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 104 (0.89x)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1911/1860 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 113 (0.97x)
Testing collisions (low 23-34 bits) - Worst is 31 bits: 240/232 (1.03x)
Testing distribution - Worst bias is the 17-bit window at bit 58 - 0.123%
Keyset 'Cyclic' - 8 cycles of 19 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 33/29 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 100 (0.86x)
Testing collisions (low 23-34 bits) - Worst is 28 bits: 1891/1860 (1.02x)
Testing distribution - Worst bias is the 17-bit window at bit 4 - 0.103%
Keyset 'Cyclic' - 8 cycles of 20 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 97 (0.83x)
Testing collisions (high 23-34 bits) - Worst is 28 bits: 1903/1860 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 125 (1.07x) (9)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 17-bit window at bit 32 - 0.172%
Keyset 'Cyclic' - 8 cycles of 24 bytes - 1000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 116.4, actual 116 (1.00x)
Testing collisions (high 23-34 bits) - Worst is 34 bits: 32/29 (1.10x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 116.4, actual 131 (1.13x) (15)
Testing collisions (low 23-34 bits) - Worst is 34 bits: 38/29 (1.31x)
Testing distribution - Worst bias is the 17-bit window at bit 11 - 0.148%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 49.6, actual 50 (1.01x) (1)
Testing collisions (high 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 49.6, actual 47 (0.95x)
Testing collisions (low 23-33 bits) - Worst is 33 bits: 27/24 (1.09x)
Testing distribution - Worst bias is the 16-bit window at bit 16 - 0.189%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 3483.1, actual 3479 (1.00x) (-4)
Testing collisions (high 26-39 bits) - Worst is 35 bits: 484/435 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 3483.1, actual 3493 (1.00x) (10)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 38/27 (1.40x)
Testing distribution - Worst bias is the 20-bit window at bit 11 - 0.064%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 40289.5, actual 39913 (0.99x) (-376)
Testing collisions (high 27-42 bits) - Worst is 41 bits: 81/78 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 40289.5, actual 40866 (1.01x) (577)
Testing collisions (low 27-42 bits) - Worst is 40 bits: 177/157 (1.12x)
Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.021%
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25586 (1.01x) (197)
Testing collisions (high 27-42 bits) - Worst is 38 bits: 422/397 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25263 (1.00x) (-126)
Testing collisions (low 27-42 bits) - Worst is 38 bits: 425/397 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 57 - 0.021%
Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25629 (1.01x) (240)
Testing collisions (high 27-42 bits) - Worst is 34 bits: 6431/6352 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25570 (1.01x) (181)
Testing collisions (low 27-42 bits) - Worst is 42 bits: 30/24 (1.21x)
Testing distribution - Worst bias is the 20-bit window at bit 13 - 0.031%
Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25389.0, actual 25481 (1.00x) (92)
Testing collisions (high 27-42 bits) - Worst is 40 bits: 111/99 (1.12x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25389.0, actual 25622 (1.01x) (233)
Testing collisions (low 27-42 bits) - Worst is 39 bits: 212/198 (1.07x)
Testing distribution - Worst bias is the 20-bit window at bit 110 - 0.026%
Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1878 (1.01x) (16)
Testing collisions (high 25-38 bits) - Worst is 34 bits: 490/465 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1926 (1.03x) (64)
Testing collisions (low 25-38 bits) - Worst is 38 bits: 36/29 (1.24x)
Testing distribution - Worst bias is the 19-bit window at bit 30 - 0.067%
Keyset 'Words' - 4000000 random keys of len 6-16 from password charset
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1862.1, actual 1898 (1.02x) (36)
Testing collisions (high 25-38 bits) - Worst is 36 bits: 138/116 (1.19x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1862.1, actual 1829 (0.98x) (-33)
Testing collisions (low 25-38 bits) - Worst is 25 bits: 228540/229220 (1.00x)
Testing distribution - Worst bias is the 19-bit window at bit 110 - 0.058%
Keyset 'Words' - 466569 dict words
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 25.3, actual 34 (1.34x) (9)
Testing collisions (high 22-32 bits) - Worst is 32 bits: 34/25 (1.34x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 25.3, actual 20 (0.79x)
Testing collisions (low 22-32 bits) - Worst is 26 bits: 1650/1618 (1.02x)
Testing distribution - Worst bias is the 16-bit window at bit 1 - 0.220%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 204800 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 4.9, actual 7 (1.43x) (3)
Testing collisions (high 21-29 bits) - Worst is 29 bits: 48/39 (1.23x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 4.9, actual 4 (0.82x)
Testing collisions (low 21-29 bits) - Worst is 29 bits: 49/39 (1.25x)
Testing distribution - Worst bias is the 15-bit window at bit 29 - 0.313%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 5000000 keys
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 2909.3, actual 2778 (0.95x)
Testing collisions (high 26-39 bits) - Worst is 37 bits: 96/90 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 2909.3, actual 2973 (1.02x) (64)
Testing collisions (low 26-39 bits) - Worst is 39 bits: 36/22 (1.58x)
Testing distribution - Worst bias is the 19-bit window at bit 79 - 0.055%
[[[ Keyset 'PerlinNoise' Tests ]]]
Testing 16777216 coordinates (L2) :
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 32725.4, actual 32905 (1.01x) (180)
Testing collisions (high 27-42 bits) - Worst is 40 bits: 142/127 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 32725.4, actual 32857 (1.00x) (132)
Testing collisions (low 27-42 bits) - Worst is 41 bits: 69/63 (1.08x)
Testing AV variant, 128 count with 4 spacing, 4-12:
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 1116.2, actual 1102 (0.99x) (-14)
Testing collisions (high 25-37 bits) - Worst is 36 bits: 83/69 (1.19x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 1116.2, actual 1068 (0.96x)
Testing collisions (low 25-37 bits) - Worst is 37 bits: 42/34 (1.20x)
[[[ Diff 'Differential' Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 128 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 128 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 128 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ DiffDist 'Differential Distribution' Tests ]]]
Testing bit 0
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 71/63 (1.11x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 145/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.123%
Testing bit 1
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2065/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.092%
Testing bit 2
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 442 (0.86x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4169/4090 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 538 (1.05x) (27)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 107 - 0.080%
Testing bit 3
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 28 bits: 8326/8170 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 113 - 0.077%
Testing bit 4
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 29 bits: 4107/4090 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 463 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 25 bits: 64159/64191 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.076%
Testing bit 5
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 482 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 28 bits: 8318/8170 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 534 (1.04x) (23)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 82 - 0.070%
Testing bit 6
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 127 - 0.104%
Testing bit 7
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16399/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 142/127 (1.11x)
Testing distribution - Worst bias is the 18-bit window at bit 106 - 0.081%
Testing bit 8
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 519/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 41 - 0.088%
Testing bit 9
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 148/127 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing distribution - Worst bias is the 18-bit window at bit 123 - 0.110%
Testing bit 10
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 89 - 0.083%
Testing bit 11
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 67/63 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 68 - 0.087%
Testing bit 12
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 565 (1.10x) (54)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.095%
Testing bit 13
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 471 (0.92x)
Testing collisions (low 24-36 bits) - Worst is 24 bits: 125649/125777 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 119 - 0.082%
Testing bit 14
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 83/63 (1.30x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 76/63 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.073%
Testing bit 15
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2182/2046 (1.07x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.104%
Testing bit 16
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 277/255 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 108 - 0.101%
Testing bit 17
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 480 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 30 - 0.090%
Testing bit 18
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 73/63 (1.14x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 44/31 (1.38x)
Testing distribution - Worst bias is the 18-bit window at bit 81 - 0.101%
Testing bit 19
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 563 (1.10x) (52)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 72/63 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.106%
Testing bit 20
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1041/1023 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 548 (1.07x) (37)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 61 - 0.071%
Testing bit 21
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 121 - 0.092%
Testing bit 22
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 63947/64191 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 135/127 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 35 - 0.111%
Testing bit 23
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 555 (1.08x) (44)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 555/511 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.096%
Testing bit 24
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 486 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 25 bits: 64468/64191 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.109%
Testing bit 25
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.081%
Testing bit 26
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 485 (0.95x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16451/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 524 (1.02x) (13)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 75 - 0.093%
Testing bit 27
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16466/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 513 (1.00x) (2)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x)
Testing distribution - Worst bias is the 18-bit window at bit 107 - 0.098%
Testing bit 28
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1060/1023 (1.04x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (low 24-36 bits) - Worst is 31 bits: 1027/1023 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 80 - 0.107%
Testing bit 29
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 507 (0.99x) (-4)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126291/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 132/127 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 126 - 0.088%
Testing bit 30
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 516 (1.01x) (5)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 516/511 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.084%
Testing bit 31
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 144/127 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.070%
Testing bit 32
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2072/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.086%
Testing bit 33
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 147/127 (1.15x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 568 (1.11x) (57)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 79/63 (1.23x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.097%
Testing bit 34
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 495 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 257/255 (1.00x)
Testing distribution - Worst bias is the 18-bit window at bit 79 - 0.087%
Testing bit 35
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 96 - 0.066%
Testing bit 36
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 77/63 (1.20x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2062/2046 (1.01x)
Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.121%
Testing bit 37
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 121 - 0.093%
Testing bit 38
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1045/1023 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 556 (1.09x) (45)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 556/511 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 123 - 0.077%
Testing bit 39
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 470 (0.92x)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 83/63 (1.30x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 282/255 (1.10x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.081%
Testing bit 40
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 493 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 27 bits: 16404/16298 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 459 (0.90x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 26 - 0.095%
Testing bit 41
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 136/127 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 270/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.100%
Testing bit 42
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 529 (1.03x) (18)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2094/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 99 - 0.076%
Testing bit 43
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 475 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 125780/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 276/255 (1.08x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 44
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 30 bits: 2095/2046 (1.02x)
Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.068%
Testing bit 45
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 558 (1.09x) (47)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x)
Testing distribution - Worst bias is the 18-bit window at bit 117 - 0.078%
Testing bit 46
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1087/1023 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 271/255 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.104%
Testing bit 47
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 491 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2085/2046 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 82 - 0.085%
Testing bit 48
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 523 (1.02x) (12)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 78/63 (1.22x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 73 - 0.081%
Testing bit 49
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2143/2046 (1.05x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 74/63 (1.16x)
Testing distribution - Worst bias is the 18-bit window at bit 66 - 0.099%
Testing bit 50
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 66/63 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 119 - 0.103%
Testing bit 51
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 502 (0.98x) (-9)
Testing collisions (high 24-36 bits) - Worst is 35 bits: 75/63 (1.17x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 266/255 (1.04x)
Testing distribution - Worst bias is the 18-bit window at bit 78 - 0.090%
Testing bit 52
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 504 (0.98x) (-7)
Testing collisions (high 24-36 bits) - Worst is 30 bits: 2076/2046 (1.01x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 477 (0.93x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x)
Testing distribution - Worst bias is the 18-bit window at bit 28 - 0.110%
Testing bit 53
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 517 (1.01x) (6)
Testing collisions (high 24-36 bits) - Worst is 34 bits: 138/127 (1.08x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x)
Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.073%
Testing bit 54
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 33/31 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 71 - 0.098%
Testing bit 55
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 527 (1.03x) (16)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x)
Testing distribution - Worst bias is the 18-bit window at bit 87 - 0.087%
Testing bit 56
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 45/31 (1.41x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 41/31 (1.28x)
Testing distribution - Worst bias is the 18-bit window at bit 17 - 0.112%
Testing bit 57
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 475 (0.93x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126113/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 539 (1.05x) (28)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 140/127 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.089%
Testing bit 58
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 557 (1.09x) (46)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17)
Testing collisions (low 24-36 bits) - Worst is 33 bits: 269/255 (1.05x)
Testing distribution - Worst bias is the 18-bit window at bit 20 - 0.091%
Testing bit 59
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7)
Testing collisions (high 24-36 bits) - Worst is 31 bits: 1082/1023 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 541 (1.06x) (30)
Testing collisions (low 24-36 bits) - Worst is 34 bits: 146/127 (1.14x)
Testing distribution - Worst bias is the 18-bit window at bit 55 - 0.065%
Testing bit 60
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 508 (0.99x) (-3)
Testing collisions (high 24-36 bits) - Worst is 33 bits: 260/255 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 488 (0.95x)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 34/31 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.083%
Testing bit 61
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 521 (1.02x) (10)
Testing collisions (high 24-36 bits) - Worst is 36 bits: 48/31 (1.50x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15)
Testing collisions (low 24-36 bits) - Worst is 32 bits: 526/511 (1.03x)
Testing distribution - Worst bias is the 18-bit window at bit 12 - 0.083%
Testing bit 62
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 492 (0.96x)
Testing collisions (high 24-36 bits) - Worst is 24 bits: 126044/125777 (1.00x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 530 (1.04x) (19)
Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x)
Testing distribution - Worst bias is the 18-bit window at bit 37 - 0.095%
Testing bit 63
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 511.9, actual 543 (1.06x) (32)
Testing collisions (high 24-36 bits) - Worst is 32 bits: 543/511 (1.06x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11)
Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x)
Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.077%
[[[ MomentChi2 Tests ]]]
Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ...
Target values to approximate : 38918200.000000 - 273633.333333
Popcount 1 stats : 38918998.920034 - 273638.558723
Popcount 0 stats : 38918770.531038 - 273645.996303
MomentChi2 for bits 1 : 1.16628
MomentChi2 for bits 0 : 0.594771
Derivative stats (transition from 2 consecutive values) :
Popcount 1 stats : 38919257.619283 - 273628.012809
Popcount 0 stats : 38918472.697479 - 273650.071385
MomentChi2 for deriv b1 : 2.04392
MomentChi2 for deriv b0 : 0.135878
Great
[[[ Prng Tests ]]]
Generating 33554432 random numbers :
Testing collisions (128-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (high 32-bit) - Expected 130731.3, actual 131029 (1.00x) (298)
Testing collisions (high 28-44 bits) - Worst is 37 bits: 4158/4095 (1.02x)
Testing collisions (low 64-bit) - Expected 0.0, actual 0 (0.00x)
Testing collisions (low 32-bit) - Expected 130731.3, actual 130672 (1.00x) (-59)
Testing collisions (low 28-44 bits) - Worst is 43 bits: 85/63 (1.33x)
[[[ BadSeeds Tests ]]]
0x0 PASS
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 1063.513065 seconds
------------------------------------------------------------
```
------------------------------------------------------------
## Pseudo-Random Function Testing
Testing the Pseudo-Random Number Generator (PRNG) Functions isn't somewhat of a chore, as a complete test could take a week or so to complete and my laptop wasn't designed for such long running intensive tasks.
Tests were conducted by utilizing [PractRand](https://pracrand.sourceforge.net) as well as some tests adopted from the [xoshiro](http://xoshiro.di.unimi.it/hwd.php) testing code, running both up to 2TB of random data (which took about 1 night for PractRand).
Results are as follows:
------------------------------------------------------------
### `fio_rand`
The following are the tests for the core `fio_rand64` and `fio_rand_bytes` functions provided when using `FIO_RAND`.
**The `PractRand` results**:
```txt
# ./tmp/rnd -p fio | RNG_test stdin
RNG_test using PractRand version 0.95
RNG = RNG_stdin, seed = unknown
test set = core, folding = standard(unknown format)
rng=RNG_stdin, seed=unknown
length= 256 megabytes (2^28 bytes), time= 3.0 seconds
no anomalies in 217 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 megabytes (2^29 bytes), time= 6.5 seconds
no anomalies in 232 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 gigabyte (2^30 bytes), time= 13.0 seconds
no anomalies in 251 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 25.2 seconds
no anomalies in 269 test result(s)
rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 48.2 seconds
no anomalies in 283 test result(s)
rng=RNG_stdin, seed=unknown
length= 8 gigabytes (2^33 bytes), time= 96.5 seconds
Test Name Raw Processed Evaluation
BCFN(2+7,13-2,T) R= -9.1 p =1-3.9e-5 unusual
...and 299 test result(s) without anomalies
rng=RNG_stdin, seed=unknown
length= 16 gigabytes (2^34 bytes), time= 190 seconds
no anomalies in 315 test result(s)
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 376 seconds
no anomalies in 328 test result(s)
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 752 seconds
no anomalies in 344 test result(s)
rng=RNG_stdin, seed=unknown
length= 128 gigabytes (2^37 bytes), time= 1498 seconds
no anomalies in 359 test result(s)
rng=RNG_stdin, seed=unknown
length= 256 gigabytes (2^38 bytes), time= 2978 seconds
no anomalies in 372 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 gigabytes (2^39 bytes), time= 9897 seconds
no anomalies in 387 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 terabyte (2^40 bytes), time= 21004 seconds
no anomalies in 401 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 terabytes (2^41 bytes), time= 43108 seconds
no anomalies in 413 test result(s)
```
**The tests adopted from the `xoshiro` code base**:
```txt
# ./tmp/random fio
mix3 extreme = 1.80533 (sig = 00100000) weight 1 (16), p-value = 0.692
mix3 extreme = 2.35650 (sig = 00000102) weight 2 (112), p-value = 0.876
mix3 extreme = 3.33991 (sig = 20002100) weight 3 (448), p-value = 0.313
mix3 extreme = 4.30088 (sig = 02210200) weight 4 (1120), p-value = 0.0189
mix3 extreme = 4.03228 (sig = 01002212) weight >=5 (4864), p-value = 0.236
bits per word = 64 (analyzing bits); min category p-value = 0.0189
processed 1.11e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.0909
------
mix3 extreme = 1.84047 (sig = 20000000) weight 1 (16), p-value = 0.663
mix3 extreme = 3.12258 (sig = 00000102) weight 2 (112), p-value = 0.182
mix3 extreme = 3.47031 (sig = 20002100) weight 3 (448), p-value = 0.208
mix3 extreme = 3.61471 (sig = 12000101) weight 4 (1120), p-value = 0.286
mix3 extreme = 3.76213 (sig = 10111202) weight >=5 (4864), p-value = 0.559
bits per word = 64 (analyzing bits); min category p-value = 0.182
processed 1.29e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.634
------
mix3 extreme = 1.65805 (sig = 00000002) weight 1 (16), p-value = 0.806
mix3 extreme = 3.09464 (sig = 00000102) weight 2 (112), p-value = 0.198
mix3 extreme = 3.53210 (sig = 00102010) weight 3 (448), p-value = 0.169
mix3 extreme = 3.35900 (sig = 02210200) weight 4 (1120), p-value = 0.584
mix3 extreme = 3.58826 (sig = 02011101) weight >=5 (4864), p-value = 0.802
bits per word = 64 (analyzing bits); min category p-value = 0.169
processed 1.66e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.603
------
mix3 extreme = 1.75332 (sig = 00000001) weight 1 (16), p-value = 0.735
mix3 extreme = 2.97345 (sig = 00000102) weight 2 (112), p-value = 0.281
mix3 extreme = 3.21121 (sig = 20002100) weight 3 (448), p-value = 0.447
mix3 extreme = 2.99207 (sig = 10102020) weight 4 (1120), p-value = 0.955
mix3 extreme = 3.53616 (sig = 21120101) weight >=5 (4864), p-value = 0.861
bits per word = 64 (analyzing bits); min category p-value = 0.281
processed 1.85e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.808
------
mix3 extreme = 1.83225 (sig = 00000100) weight 1 (16), p-value = 0.67
mix3 extreme = 3.10258 (sig = 00000102) weight 2 (112), p-value = 0.194
mix3 extreme = 3.03291 (sig = 20002100) weight 3 (448), p-value = 0.663
mix3 extreme = 3.09549 (sig = 10200022) weight 4 (1120), p-value = 0.89
mix3 extreme = 3.76428 (sig = 02011101) weight >=5 (4864), p-value = 0.556
bits per word = 64 (analyzing bits); min category p-value = 0.194
processed 2.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.659
------
mix3 extreme = 1.84289 (sig = 00000100) weight 1 (16), p-value = 0.661
mix3 extreme = 3.57529 (sig = 00000102) weight 2 (112), p-value = 0.0384
mix3 extreme = 3.46502 (sig = 20002010) weight 3 (448), p-value = 0.211
mix3 extreme = 3.25764 (sig = 00220201) weight 4 (1120), p-value = 0.716
mix3 extreme = 4.19914 (sig = 11222212) weight >=5 (4864), p-value = 0.122
bits per word = 64 (analyzing bits); min category p-value = 0.0384
processed 2.59e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.178
------
mix3 extreme = 2.19203 (sig = 00000002) weight 1 (16), p-value = 0.369
mix3 extreme = 3.04970 (sig = 00000102) weight 2 (112), p-value = 0.227
mix3 extreme = 3.44778 (sig = 20002010) weight 3 (448), p-value = 0.224
mix3 extreme = 3.66866 (sig = 22110000) weight 4 (1120), p-value = 0.239
mix3 extreme = 3.58667 (sig = 02021021) weight >=5 (4864), p-value = 0.804
bits per word = 64 (analyzing bits); min category p-value = 0.224
processed 3.14e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.718
------
mix3 extreme = 2.51064 (sig = 00000002) weight 1 (16), p-value = 0.176
mix3 extreme = 2.57871 (sig = 20010000) weight 2 (112), p-value = 0.672
mix3 extreme = 3.85273 (sig = 20002010) weight 3 (448), p-value = 0.051
mix3 extreme = 3.18862 (sig = 01101010) weight 4 (1120), p-value = 0.799
mix3 extreme = 3.60146 (sig = 22121010) weight >=5 (4864), p-value = 0.785
bits per word = 64 (analyzing bits); min category p-value = 0.051
processed 4.07e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:21 2025
p = 0.23
------
mix3 extreme = 1.89834 (sig = 00000002) weight 1 (16), p-value = 0.613
mix3 extreme = 2.27677 (sig = 00100200) weight 2 (112), p-value = 0.924
mix3 extreme = 3.52766 (sig = 20000022) weight 3 (448), p-value = 0.171
mix3 extreme = 3.50133 (sig = 11000102) weight 4 (1120), p-value = 0.405
mix3 extreme = 3.47778 (sig = 11121011) weight >=5 (4864), p-value = 0.915
bits per word = 64 (analyzing bits); min category p-value = 0.171
processed 5.18e+08 bytes in 1 seconds (0.5177 GB/s, 1.864 TB/h). Fri Feb 21 23:22:22 2025
p = 0.609
------
mix3 extreme = 1.99814 (sig = 00000001) weight 1 (16), p-value = 0.527
mix3 extreme = 2.43362 (sig = 00100200) weight 2 (112), p-value = 0.815
mix3 extreme = 3.46309 (sig = 20002010) weight 3 (448), p-value = 0.213
mix3 extreme = 3.67295 (sig = 20110001) weight 4 (1120), p-value = 0.236
mix3 extreme = 3.50848 (sig = 00222210) weight >=5 (4864), p-value = 0.888
bits per word = 64 (analyzing bits); min category p-value = 0.213
processed 6.1e+08 bytes in 1 seconds (0.6101 GB/s, 2.196 TB/h). Fri Feb 21 23:22:22 2025
p = 0.698
------
mix3 extreme = 1.78022 (sig = 00000002) weight 1 (16), p-value = 0.713
mix3 extreme = 2.64570 (sig = 10000001) weight 2 (112), p-value = 0.6
mix3 extreme = 3.55344 (sig = 20002010) weight 3 (448), p-value = 0.157
mix3 extreme = 3.15383 (sig = 11000102) weight 4 (1120), p-value = 0.836
mix3 extreme = 4.03858 (sig = 11121011) weight >=5 (4864), p-value = 0.23
bits per word = 64 (analyzing bits); min category p-value = 0.157
processed 7.03e+08 bytes in 1 seconds (0.7026 GB/s, 2.529 TB/h). Fri Feb 21 23:22:22 2025
p = 0.573
------
mix3 extreme = 1.83987 (sig = 00000002) weight 1 (16), p-value = 0.663
mix3 extreme = 2.43194 (sig = 00000110) weight 2 (112), p-value = 0.816
mix3 extreme = 3.40962 (sig = 20000022) weight 3 (448), p-value = 0.253
mix3 extreme = 3.02964 (sig = 02012200) weight 4 (1120), p-value = 0.936
mix3 extreme = 3.75207 (sig = 20222201) weight >=5 (4864), p-value = 0.574
bits per word = 64 (analyzing bits); min category p-value = 0.253
processed 8.5e+08 bytes in 1 seconds (0.8505 GB/s, 3.062 TB/h). Fri Feb 21 23:22:22 2025
p = 0.767
------
mix3 extreme = 2.03491 (sig = 00000002) weight 1 (16), p-value = 0.496
mix3 extreme = 2.48334 (sig = 00001002) weight 2 (112), p-value = 0.769
mix3 extreme = 3.15042 (sig = 10200002) weight 3 (448), p-value = 0.519
mix3 extreme = 3.19215 (sig = 21200001) weight 4 (1120), p-value = 0.795
mix3 extreme = 3.68390 (sig = 20222201) weight >=5 (4864), p-value = 0.673
bits per word = 64 (analyzing bits); min category p-value = 0.496
processed 1.02e+09 bytes in 1 seconds (1.017 GB/s, 3.661 TB/h). Fri Feb 21 23:22:22 2025
p = 0.967
------
mix3 extreme = 1.98184 (sig = 00000001) weight 1 (16), p-value = 0.541
mix3 extreme = 2.49397 (sig = 01001000) weight 2 (112), p-value = 0.759
mix3 extreme = 2.98386 (sig = 02000011) weight 3 (448), p-value = 0.721
mix3 extreme = 3.29022 (sig = 20200220) weight 4 (1120), p-value = 0.674
mix3 extreme = 4.30382 (sig = 11122011) weight >=5 (4864), p-value = 0.0784
bits per word = 64 (analyzing bits); min category p-value = 0.0784
processed 1.26e+09 bytes in 1 seconds (1.257 GB/s, 4.526 TB/h). Fri Feb 21 23:22:22 2025
p = 0.335
------
mix3 extreme = 2.33599 (sig = 00000001) weight 1 (16), p-value = 0.27
mix3 extreme = 2.45643 (sig = 00000110) weight 2 (112), p-value = 0.795
mix3 extreme = 2.84156 (sig = 00022100) weight 3 (448), p-value = 0.867
mix3 extreme = 3.61210 (sig = 01100202) weight 4 (1120), p-value = 0.288
mix3 extreme = 3.51573 (sig = 21101210) weight >=5 (4864), p-value = 0.882
bits per word = 64 (analyzing bits); min category p-value = 0.27
processed 1.52e+09 bytes in 1 seconds (1.516 GB/s, 5.458 TB/h). Fri Feb 21 23:22:22 2025
p = 0.793
------
mix3 extreme = 2.57590 (sig = 00000001) weight 1 (16), p-value = 0.149
mix3 extreme = 2.18056 (sig = 10000002) weight 2 (112), p-value = 0.964
mix3 extreme = 2.94943 (sig = 20102000) weight 3 (448), p-value = 0.76
mix3 extreme = 3.39387 (sig = 11010002) weight 4 (1120), p-value = 0.538
mix3 extreme = 3.85744 (sig = 22210212) weight >=5 (4864), p-value = 0.427
bits per word = 64 (analyzing bits); min category p-value = 0.149
processed 1.76e+09 bytes in 1 seconds (1.756 GB/s, 6.323 TB/h). Fri Feb 21 23:22:22 2025
p = 0.552
------
mix3 extreme = 2.64879 (sig = 00000001) weight 1 (16), p-value = 0.122
mix3 extreme = 2.69778 (sig = 10000002) weight 2 (112), p-value = 0.544
mix3 extreme = 3.15759 (sig = 20102000) weight 3 (448), p-value = 0.51
mix3 extreme = 3.41194 (sig = 11010002) weight 4 (1120), p-value = 0.515
mix3 extreme = 4.19052 (sig = 11122011) weight >=5 (4864), p-value = 0.127
bits per word = 64 (analyzing bits); min category p-value = 0.122
processed 2.02e+09 bytes in 1 seconds (2.015 GB/s, 7.255 TB/h). Fri Feb 21 23:22:22 2025
p = 0.477
------
mix3 extreme = 2.10150 (sig = 00000001) weight 1 (16), p-value = 0.44
mix3 extreme = 2.31937 (sig = 00100100) weight 2 (112), p-value = 0.9
mix3 extreme = 2.98427 (sig = 20102000) weight 3 (448), p-value = 0.721
mix3 extreme = 3.62790 (sig = 21200001) weight 4 (1120), p-value = 0.274
mix3 extreme = 3.47379 (sig = 22011100) weight >=5 (4864), p-value = 0.918
bits per word = 64 (analyzing bits); min category p-value = 0.274
processed 2.51e+09 bytes in 1 seconds (2.514 GB/s, 9.052 TB/h). Fri Feb 21 23:22:22 2025
p = 0.798
------
mix3 extreme = 2.20184 (sig = 00100000) weight 1 (16), p-value = 0.362
mix3 extreme = 2.44576 (sig = 00100100) weight 2 (112), p-value = 0.804
mix3 extreme = 2.66267 (sig = 10200002) weight 3 (448), p-value = 0.969
mix3 extreme = 3.76139 (sig = 21200001) weight 4 (1120), p-value = 0.172
mix3 extreme = 3.82048 (sig = 20222201) weight >=5 (4864), p-value = 0.477
bits per word = 64 (analyzing bits); min category p-value = 0.172
processed 3.01e+09 bytes in 2 seconds (1.507 GB/s, 5.425 TB/h). Fri Feb 21 23:22:23 2025
p = 0.612
------
mix3 extreme = 2.18426 (sig = 00100000) weight 1 (16), p-value = 0.375
mix3 extreme = 2.65062 (sig = 20002000) weight 2 (112), p-value = 0.595
mix3 extreme = 2.92917 (sig = 01100200) weight 3 (448), p-value = 0.782
mix3 extreme = 3.89526 (sig = 21200001) weight 4 (1120), p-value = 0.104
mix3 extreme = 3.78679 (sig = 20220022) weight >=5 (4864), p-value = 0.524
bits per word = 64 (analyzing bits); min category p-value = 0.104
processed 4.01e+09 bytes in 2 seconds (2.006 GB/s, 7.222 TB/h). Fri Feb 21 23:22:23 2025
p = 0.423
------
mix3 extreme = 2.46216 (sig = 20000000) weight 1 (16), p-value = 0.199
mix3 extreme = 2.30564 (sig = 20002000) weight 2 (112), p-value = 0.909
mix3 extreme = 2.97563 (sig = 00100220) weight 3 (448), p-value = 0.731
mix3 extreme = 3.70435 (sig = 20110001) weight 4 (1120), p-value = 0.211
mix3 extreme = 3.47865 (sig = 22100011) weight >=5 (4864), p-value = 0.914
bits per word = 64 (analyzing bits); min category p-value = 0.199
processed 5.01e+09 bytes in 2 seconds (2.505 GB/s, 9.019 TB/h). Fri Feb 21 23:22:23 2025
p = 0.671
------
mix3 extreme = 2.48402 (sig = 00100000) weight 1 (16), p-value = 0.189
mix3 extreme = 2.32620 (sig = 00000012) weight 2 (112), p-value = 0.896
mix3 extreme = 3.13986 (sig = 00120200) weight 3 (448), p-value = 0.531
mix3 extreme = 3.56935 (sig = 20110001) weight 4 (1120), p-value = 0.33
mix3 extreme = 3.62670 (sig = 01210201) weight >=5 (4864), p-value = 0.753
bits per word = 64 (analyzing bits); min category p-value = 0.189
processed 6.01e+09 bytes in 3 seconds (2.003 GB/s, 7.211 TB/h). Fri Feb 21 23:22:24 2025
p = 0.649
------
mix3 extreme = 2.07995 (sig = 00100000) weight 1 (16), p-value = 0.458
mix3 extreme = 2.14438 (sig = 00002010) weight 2 (112), p-value = 0.974
mix3 extreme = 3.12213 (sig = 00120200) weight 3 (448), p-value = 0.553
mix3 extreme = 3.59330 (sig = 20110001) weight 4 (1120), p-value = 0.306
mix3 extreme = 3.93490 (sig = 01210201) weight >=5 (4864), p-value = 0.333
bits per word = 64 (analyzing bits); min category p-value = 0.306
processed 7.01e+09 bytes in 3 seconds (2.336 GB/s, 8.409 TB/h). Fri Feb 21 23:22:24 2025
p = 0.839
------
mix3 extreme = 1.82939 (sig = 20000000) weight 1 (16), p-value = 0.672
mix3 extreme = 2.04212 (sig = 00002010) weight 2 (112), p-value = 0.991
mix3 extreme = 3.40400 (sig = 00120200) weight 3 (448), p-value = 0.257
mix3 extreme = 3.20336 (sig = 12020002) weight 4 (1120), p-value = 0.782
mix3 extreme = 4.26991 (sig = 01210201) weight >=5 (4864), p-value = 0.0907
bits per word = 64 (analyzing bits); min category p-value = 0.0907
processed 8.5e+09 bytes in 4 seconds (2.126 GB/s, 7.654 TB/h). Fri Feb 21 23:22:25 2025
p = 0.378
------
mix3 extreme = 1.82510 (sig = 00100000) weight 1 (16), p-value = 0.676
mix3 extreme = 2.69933 (sig = 00002020) weight 2 (112), p-value = 0.542
mix3 extreme = 2.78787 (sig = 02020200) weight 3 (448), p-value = 0.908
mix3 extreme = 3.06049 (sig = 00001122) weight 4 (1120), p-value = 0.916
mix3 extreme = 4.40242 (sig = 01210201) weight >=5 (4864), p-value = 0.0507
bits per word = 64 (analyzing bits); min category p-value = 0.0507
processed 1e+10 bytes in 4 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:25 2025
p = 0.229
------
mix3 extreme = 2.20665 (sig = 00100000) weight 1 (16), p-value = 0.358
mix3 extreme = 2.66682 (sig = 00002020) weight 2 (112), p-value = 0.577
mix3 extreme = 3.18173 (sig = 02200010) weight 3 (448), p-value = 0.481
mix3 extreme = 3.39020 (sig = 01202100) weight 4 (1120), p-value = 0.543
mix3 extreme = 3.99569 (sig = 01210201) weight >=5 (4864), p-value = 0.269
bits per word = 64 (analyzing bits); min category p-value = 0.269
processed 1.25e+10 bytes in 5 seconds (2.503 GB/s, 9.012 TB/h). Fri Feb 21 23:22:26 2025
p = 0.792
------
mix3 extreme = 1.60923 (sig = 00000002) weight 1 (16), p-value = 0.838
mix3 extreme = 2.73755 (sig = 02000001) weight 2 (112), p-value = 0.501
mix3 extreme = 3.23640 (sig = 00101200) weight 3 (448), p-value = 0.419
mix3 extreme = 3.31439 (sig = 21220000) weight 4 (1120), p-value = 0.643
mix3 extreme = 3.57996 (sig = 21001101) weight >=5 (4864), p-value = 0.812
bits per word = 64 (analyzing bits); min category p-value = 0.419
processed 1.5e+10 bytes in 6 seconds (2.502 GB/s, 9.008 TB/h). Fri Feb 21 23:22:27 2025
p = 0.934
------
mix3 extreme = 1.91785 (sig = 00000002) weight 1 (16), p-value = 0.596
mix3 extreme = 2.60874 (sig = 02000001) weight 2 (112), p-value = 0.64
mix3 extreme = 3.38156 (sig = 00101200) weight 3 (448), p-value = 0.276
mix3 extreme = 3.24044 (sig = 21220000) weight 4 (1120), p-value = 0.737
mix3 extreme = 3.69590 (sig = 12211201) weight >=5 (4864), p-value = 0.656
bits per word = 64 (analyzing bits); min category p-value = 0.276
processed 1.75e+10 bytes in 7 seconds (2.501 GB/s, 9.004 TB/h). Fri Feb 21 23:22:28 2025
p = 0.801
------
mix3 extreme = 1.80532 (sig = 00000001) weight 1 (16), p-value = 0.692
mix3 extreme = 2.54416 (sig = 00020010) weight 2 (112), p-value = 0.709
mix3 extreme = 3.15400 (sig = 00101200) weight 3 (448), p-value = 0.514
mix3 extreme = 3.33906 (sig = 21220000) weight 4 (1120), p-value = 0.61
mix3 extreme = 3.56547 (sig = 00012112) weight >=5 (4864), p-value = 0.829
bits per word = 64 (analyzing bits); min category p-value = 0.514
processed 2e+10 bytes in 8 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:29 2025
p = 0.973
------
mix3 extreme = 1.94039 (sig = 00000002) weight 1 (16), p-value = 0.577
mix3 extreme = 2.27077 (sig = 01010000) weight 2 (112), p-value = 0.928
mix3 extreme = 3.19342 (sig = 00120200) weight 3 (448), p-value = 0.468
mix3 extreme = 3.51655 (sig = 21220000) weight 4 (1120), p-value = 0.387
mix3 extreme = 3.56607 (sig = 20122012) weight >=5 (4864), p-value = 0.828
bits per word = 64 (analyzing bits); min category p-value = 0.387
processed 2.5e+10 bytes in 10 seconds (2.502 GB/s, 9.005 TB/h). Fri Feb 21 23:22:31 2025
p = 0.914
------
mix3 extreme = 1.89200 (sig = 01000000) weight 1 (16), p-value = 0.619
mix3 extreme = 2.24801 (sig = 01010000) weight 2 (112), p-value = 0.938
mix3 extreme = 2.88784 (sig = 02000210) weight 3 (448), p-value = 0.825
mix3 extreme = 3.34758 (sig = 21220000) weight 4 (1120), p-value = 0.599
mix3 extreme = 3.45248 (sig = 11200111) weight >=5 (4864), p-value = 0.933
bits per word = 64 (analyzing bits); min category p-value = 0.599
processed 3e+10 bytes in 12 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:33 2025
p = 0.99
------
mix3 extreme = 1.56396 (sig = 00000200) weight 1 (16), p-value = 0.865
mix3 extreme = 1.97760 (sig = 00000022) weight 2 (112), p-value = 0.996
mix3 extreme = 3.23027 (sig = 20021000) weight 3 (448), p-value = 0.426
mix3 extreme = 3.36856 (sig = 21021000) weight 4 (1120), p-value = 0.571
mix3 extreme = 4.27622 (sig = 20122012) weight >=5 (4864), p-value = 0.0883
bits per word = 64 (analyzing bits); min category p-value = 0.0883
processed 4e+10 bytes in 16 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:37 2025
p = 0.37
------
mix3 extreme = 1.89666 (sig = 00000100) weight 1 (16), p-value = 0.615
mix3 extreme = 2.70819 (sig = 20000010) weight 2 (112), p-value = 0.532
mix3 extreme = 3.11747 (sig = 20021000) weight 3 (448), p-value = 0.559
mix3 extreme = 2.83492 (sig = 21021000) weight 4 (1120), p-value = 0.994
mix3 extreme = 3.73280 (sig = 20122012) weight >=5 (4864), p-value = 0.602
bits per word = 64 (analyzing bits); min category p-value = 0.532
processed 5e+10 bytes in 20 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:41 2025
p = 0.978
------
mix3 extreme = 1.60871 (sig = 00000001) weight 1 (16), p-value = 0.838
mix3 extreme = 2.92820 (sig = 20000010) weight 2 (112), p-value = 0.318
mix3 extreme = 3.30722 (sig = 20021000) weight 3 (448), p-value = 0.344
mix3 extreme = 3.12534 (sig = 12100002) weight 4 (1120), p-value = 0.863
mix3 extreme = 3.90592 (sig = 21101011) weight >=5 (4864), p-value = 0.367
bits per word = 64 (analyzing bits); min category p-value = 0.318
processed 6e+10 bytes in 24 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:45 2025
p = 0.852
------
mix3 extreme = 1.48010 (sig = 02000000) weight 1 (16), p-value = 0.909
mix3 extreme = 2.48996 (sig = 20000010) weight 2 (112), p-value = 0.763
mix3 extreme = 3.17803 (sig = 20021000) weight 3 (448), p-value = 0.486
mix3 extreme = 3.17420 (sig = 20101010) weight 4 (1120), p-value = 0.814
mix3 extreme = 3.79968 (sig = 21101011) weight >=5 (4864), p-value = 0.506
bits per word = 64 (analyzing bits); min category p-value = 0.486
processed 7e+10 bytes in 28 seconds (2.501 GB/s, 9.002 TB/h). Fri Feb 21 23:22:49 2025
p = 0.964
------
mix3 extreme = 2.31418 (sig = 02000000) weight 1 (16), p-value = 0.284
mix3 extreme = 2.75073 (sig = 02000001) weight 2 (112), p-value = 0.487
mix3 extreme = 3.22184 (sig = 20021000) weight 3 (448), p-value = 0.435
mix3 extreme = 2.99913 (sig = 12100002) weight 4 (1120), p-value = 0.952
mix3 extreme = 4.17272 (sig = 21101011) weight >=5 (4864), p-value = 0.136
bits per word = 64 (analyzing bits); min category p-value = 0.136
processed 8.5e+10 bytes in 35 seconds (2.429 GB/s, 8.744 TB/h). Fri Feb 21 23:22:56 2025
p = 0.519
------
mix3 extreme = 2.96167 (sig = 02000000) weight 1 (16), p-value = 0.0478
mix3 extreme = 2.15955 (sig = 02000001) weight 2 (112), p-value = 0.97
mix3 extreme = 3.37730 (sig = 20021000) weight 3 (448), p-value = 0.28
mix3 extreme = 3.52700 (sig = 12100002) weight 4 (1120), p-value = 0.376
mix3 extreme = 4.04580 (sig = 21101011) weight >=5 (4864), p-value = 0.224
bits per word = 64 (analyzing bits); min category p-value = 0.0478
processed 1e+11 bytes in 41 seconds (2.439 GB/s, 8.781 TB/h). Fri Feb 21 23:23:02 2025
p = 0.217
------
mix3 extreme = 2.47196 (sig = 02000000) weight 1 (16), p-value = 0.195
mix3 extreme = 2.51373 (sig = 02000001) weight 2 (112), p-value = 0.74
mix3 extreme = 3.39366 (sig = 20021000) weight 3 (448), p-value = 0.266
mix3 extreme = 3.21456 (sig = 12100002) weight 4 (1120), p-value = 0.769
mix3 extreme = 3.77086 (sig = 12221021) weight >=5 (4864), p-value = 0.547
bits per word = 64 (analyzing bits); min category p-value = 0.195
processed 1.25e+11 bytes in 51 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:23:12 2025
p = 0.661
------
mix3 extreme = 2.48207 (sig = 02000000) weight 1 (16), p-value = 0.19
mix3 extreme = 2.29109 (sig = 20020000) weight 2 (112), p-value = 0.917
mix3 extreme = 3.01591 (sig = 20021000) weight 3 (448), p-value = 0.683
mix3 extreme = 3.36633 (sig = 20200101) weight 4 (1120), p-value = 0.574
mix3 extreme = 3.40749 (sig = 02222012) weight >=5 (4864), p-value = 0.959
bits per word = 64 (analyzing bits); min category p-value = 0.19
processed 1.5e+11 bytes in 61 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:23:22 2025
p = 0.651
------
mix3 extreme = 2.14788 (sig = 02000000) weight 1 (16), p-value = 0.403
mix3 extreme = 2.23188 (sig = 01000002) weight 2 (112), p-value = 0.945
mix3 extreme = 3.01619 (sig = 20100002) weight 3 (448), p-value = 0.683
mix3 extreme = 3.35531 (sig = 22020010) weight 4 (1120), p-value = 0.589
mix3 extreme = 3.90091 (sig = 20120120) weight >=5 (4864), p-value = 0.373
bits per word = 64 (analyzing bits); min category p-value = 0.373
processed 1.75e+11 bytes in 71 seconds (2.465 GB/s, 8.874 TB/h). Fri Feb 21 23:23:32 2025
p = 0.903
------
mix3 extreme = 2.56990 (sig = 00010000) weight 1 (16), p-value = 0.151
mix3 extreme = 2.15879 (sig = 20020000) weight 2 (112), p-value = 0.97
mix3 extreme = 3.11637 (sig = 20100002) weight 3 (448), p-value = 0.56
mix3 extreme = 3.75031 (sig = 20200101) weight 4 (1120), p-value = 0.179
mix3 extreme = 3.82089 (sig = 11022021) weight >=5 (4864), p-value = 0.476
bits per word = 64 (analyzing bits); min category p-value = 0.151
processed 2e+11 bytes in 81 seconds (2.469 GB/s, 8.889 TB/h). Fri Feb 21 23:23:42 2025
p = 0.559
------
mix3 extreme = 2.68286 (sig = 00010000) weight 1 (16), p-value = 0.111
mix3 extreme = 2.03612 (sig = 01000002) weight 2 (112), p-value = 0.992
mix3 extreme = 2.72508 (sig = 01110000) weight 3 (448), p-value = 0.944
mix3 extreme = 3.29533 (sig = 20200101) weight 4 (1120), p-value = 0.668
mix3 extreme = 3.94062 (sig = 11101011) weight >=5 (4864), p-value = 0.327
bits per word = 64 (analyzing bits); min category p-value = 0.111
processed 2.5e+11 bytes in 102 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:24:03 2025
p = 0.444
------
mix3 extreme = 3.02806 (sig = 00010000) weight 1 (16), p-value = 0.0387
mix3 extreme = 2.64241 (sig = 12000000) weight 2 (112), p-value = 0.604
mix3 extreme = 3.14107 (sig = 12001000) weight 3 (448), p-value = 0.53
mix3 extreme = 3.10292 (sig = 10202002) weight 4 (1120), p-value = 0.883
mix3 extreme = 3.78800 (sig = 11122221) weight >=5 (4864), p-value = 0.522
bits per word = 64 (analyzing bits); min category p-value = 0.0387
processed 3e+11 bytes in 122 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:24:23 2025
p = 0.179
------
mix3 extreme = 2.11373 (sig = 00010000) weight 1 (16), p-value = 0.43
mix3 extreme = 2.53673 (sig = 00010020) weight 2 (112), p-value = 0.716
mix3 extreme = 3.82566 (sig = 01000220) weight 3 (448), p-value = 0.0568
mix3 extreme = 3.94266 (sig = 22020010) weight 4 (1120), p-value = 0.0863
mix3 extreme = 3.38864 (sig = 02212120) weight >=5 (4864), p-value = 0.967
bits per word = 64 (analyzing bits); min category p-value = 0.0568
processed 4e+11 bytes in 163 seconds (2.454 GB/s, 8.834 TB/h). Fri Feb 21 23:25:04 2025
p = 0.253
------
mix3 extreme = 2.02087 (sig = 02000000) weight 1 (16), p-value = 0.507
mix3 extreme = 3.38282 (sig = 00010020) weight 2 (112), p-value = 0.0772
mix3 extreme = 3.77275 (sig = 01000220) weight 3 (448), p-value = 0.0698
mix3 extreme = 3.96045 (sig = 22020010) weight 4 (1120), p-value = 0.0804
mix3 extreme = 3.96698 (sig = 10212100) weight >=5 (4864), p-value = 0.298
bits per word = 64 (analyzing bits); min category p-value = 0.0698
processed 5e+11 bytes in 204 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:25:45 2025
p = 0.304
------
mix3 extreme = 2.11460 (sig = 02000000) weight 1 (16), p-value = 0.429
mix3 extreme = 3.45081 (sig = 00010020) weight 2 (112), p-value = 0.0607
mix3 extreme = 4.11176 (sig = 01000220) weight 3 (448), p-value = 0.0174
mix3 extreme = 3.90641 (sig = 22020010) weight 4 (1120), p-value = 0.0996
mix3 extreme = 3.86051 (sig = 10110212) weight >=5 (4864), p-value = 0.423
bits per word = 64 (analyzing bits); min category p-value = 0.0174
processed 6e+11 bytes in 244 seconds (2.459 GB/s, 8.853 TB/h). Fri Feb 21 23:26:25 2025
p = 0.0842
------
mix3 extreme = 2.19471 (sig = 02000000) weight 1 (16), p-value = 0.367
mix3 extreme = 3.42206 (sig = 00010020) weight 2 (112), p-value = 0.0673
mix3 extreme = 3.76615 (sig = 01000220) weight 3 (448), p-value = 0.0716
mix3 extreme = 3.59051 (sig = 22020010) weight 4 (1120), p-value = 0.309
mix3 extreme = 3.55018 (sig = 20012221) weight >=5 (4864), p-value = 0.846
bits per word = 64 (analyzing bits); min category p-value = 0.0673
processed 7e+11 bytes in 285 seconds (2.456 GB/s, 8.842 TB/h). Fri Feb 21 23:27:06 2025
p = 0.294
------
mix3 extreme = 2.08568 (sig = 02000000) weight 1 (16), p-value = 0.453
mix3 extreme = 2.70372 (sig = 00010020) weight 2 (112), p-value = 0.537
mix3 extreme = 3.67441 (sig = 20021000) weight 3 (448), p-value = 0.101
mix3 extreme = 3.40600 (sig = 22020010) weight 4 (1120), p-value = 0.522
mix3 extreme = 3.74169 (sig = 10110212) weight >=5 (4864), p-value = 0.589
bits per word = 64 (analyzing bits); min category p-value = 0.101
processed 8.5e+11 bytes in 346 seconds (2.457 GB/s, 8.844 TB/h). Fri Feb 21 23:28:07 2025
p = 0.414
------
mix3 extreme = 1.70943 (sig = 00000100) weight 1 (16), p-value = 0.768
mix3 extreme = 3.16917 (sig = 00010020) weight 2 (112), p-value = 0.157
mix3 extreme = 3.64671 (sig = 02000201) weight 3 (448), p-value = 0.112
mix3 extreme = 3.82262 (sig = 11010200) weight 4 (1120), p-value = 0.137
mix3 extreme = 3.64758 (sig = 00222021) weight >=5 (4864), p-value = 0.724
bits per word = 64 (analyzing bits); min category p-value = 0.112
processed 1e+12 bytes in 407 seconds (2.457 GB/s, 8.845 TB/h). Fri Feb 21 23:29:08 2025
p = 0.448
------
mix3 extreme = 1.76773 (sig = 20000000) weight 1 (16), p-value = 0.723
mix3 extreme = 3.14942 (sig = 00002020) weight 2 (112), p-value = 0.168
mix3 extreme = 3.45782 (sig = 02000201) weight 3 (448), p-value = 0.217
mix3 extreme = 3.68367 (sig = 11010200) weight 4 (1120), p-value = 0.227
mix3 extreme = 4.05225 (sig = 00222021) weight >=5 (4864), p-value = 0.219
bits per word = 64 (analyzing bits); min category p-value = 0.168
processed 1.25e+12 bytes in 509 seconds (2.456 GB/s, 8.841 TB/h). Fri Feb 21 23:30:50 2025
p = 0.6
------
mix3 extreme = 1.78446 (sig = 00000001) weight 1 (16), p-value = 0.709
mix3 extreme = 3.00030 (sig = 00002020) weight 2 (112), p-value = 0.261
mix3 extreme = 3.68239 (sig = 02000201) weight 3 (448), p-value = 0.0983
mix3 extreme = 3.38984 (sig = 11010020) weight 4 (1120), p-value = 0.543
mix3 extreme = 4.00734 (sig = 02221002) weight >=5 (4864), p-value = 0.258
bits per word = 64 (analyzing bits); min category p-value = 0.0983
processed 1.5e+12 bytes in 611 seconds (2.455 GB/s, 8.838 TB/h). Fri Feb 21 23:32:32 2025
p = 0.404
------
mix3 extreme = 1.49970 (sig = 02000000) weight 1 (16), p-value = 0.899
mix3 extreme = 2.64430 (sig = 20000020) weight 2 (112), p-value = 0.602
mix3 extreme = 3.85954 (sig = 02000201) weight 3 (448), p-value = 0.0496
mix3 extreme = 3.76326 (sig = 11010020) weight 4 (1120), p-value = 0.171
mix3 extreme = 3.91001 (sig = 00222021) weight >=5 (4864), p-value = 0.362
bits per word = 64 (analyzing bits); min category p-value = 0.0496
processed 1.75e+12 bytes in 713 seconds (2.454 GB/s, 8.836 TB/h). Fri Feb 21 23:34:14 2025
p = 0.225
------
mix3 extreme = 1.64313 (sig = 02000000) weight 1 (16), p-value = 0.816
mix3 extreme = 3.02382 (sig = 02100000) weight 2 (112), p-value = 0.244
mix3 extreme = 3.21441 (sig = 02000201) weight 3 (448), p-value = 0.443
mix3 extreme = 4.07811 (sig = 11010020) weight 4 (1120), p-value = 0.0496
mix3 extreme = 4.50737 (sig = 00222021) weight >=5 (4864), p-value = 0.0314
bits per word = 64 (analyzing bits); min category p-value = 0.0314
processed 2e+12 bytes in 815 seconds (2.454 GB/s, 8.834 TB/h). Fri Feb 21 23:35:56 2025
p = 0.148
------
mix3 extreme = 1.57884 (sig = 20000000) weight 1 (16), p-value = 0.857
mix3 extreme = 2.68908 (sig = 00001002) weight 2 (112), p-value = 0.553
mix3 extreme = 3.22275 (sig = 01000220) weight 3 (448), p-value = 0.434
mix3 extreme = 3.84110 (sig = 11010020) weight 4 (1120), p-value = 0.128
mix3 extreme = 3.89955 (sig = 10020112) weight >=5 (4864), p-value = 0.374
bits per word = 64 (analyzing bits); min category p-value = 0.128
processed 2.5e+12 bytes in 1.02e+03 seconds (2.456 GB/s, 8.841 TB/h). Fri Feb 21 23:39:19 2025
p = 0.496
------
mix3 extreme = 1.67736 (sig = 00001000) weight 1 (16), p-value = 0.792
mix3 extreme = 2.62488 (sig = 00001002) weight 2 (112), p-value = 0.623
mix3 extreme = 3.38419 (sig = 02002002) weight 3 (448), p-value = 0.274
mix3 extreme = 3.80757 (sig = 11010020) weight 4 (1120), p-value = 0.145
mix3 extreme = 3.95015 (sig = 00211221) weight >=5 (4864), p-value = 0.316
bits per word = 64 (analyzing bits); min category p-value = 0.145
processed 3e+12 bytes in 1.22e+03 seconds (2.451 GB/s, 8.824 TB/h). Fri Feb 21 23:42:45 2025
p = 0.544
------
mix3 extreme = 1.79534 (sig = 00002000) weight 1 (16), p-value = 0.701
mix3 extreme = 2.76965 (sig = 00001002) weight 2 (112), p-value = 0.468
mix3 extreme = 3.58785 (sig = 01000220) weight 3 (448), p-value = 0.139
mix3 extreme = 3.25341 (sig = 20200012) weight 4 (1120), p-value = 0.721
mix3 extreme = 3.65576 (sig = 12111122) weight >=5 (4864), p-value = 0.713
bits per word = 64 (analyzing bits); min category p-value = 0.139
processed 4e+12 bytes in 1.63e+03 seconds (2.448 GB/s, 8.813 TB/h). Fri Feb 21 23:49:35 2025
p = 0.526
------
```
------------------------------------------------------------
### `FIO_DEFINE_RANDOM128_FN`
The following are the tests for the built-in `FIO_DEFINE_RANDOM128_FN` macro using the deterministic PRNG (where the auto-reseeding `reseed_log` is set to `0`).
**Note**: setting `reseed_log` to any practical value (such as `31`) would improve randomness make the PRNG non-deterministic, improving the security of the result.
**The `PractRand` results**:
```txt
# ./tmp/rnd -p M128 | RNG_test stdin
RNG_test using PractRand version 0.95
RNG = RNG_stdin, seed = unknown
test set = core, folding = standard(unknown format)
rng=RNG_stdin, seed=unknown
length= 256 megabytes (2^28 bytes), time= 2.4 seconds
no anomalies in 217 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 megabytes (2^29 bytes), time= 5.0 seconds
no anomalies in 232 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 gigabyte (2^30 bytes), time= 10.1 seconds
no anomalies in 251 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 20.6 seconds
no anomalies in 269 test result(s)
rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 41.2 seconds
no anomalies in 283 test result(s)
rng=RNG_stdin, seed=unknown
length= 8 gigabytes (2^33 bytes), time= 83.9 seconds
no anomalies in 300 test result(s)
rng=RNG_stdin, seed=unknown
length= 16 gigabytes (2^34 bytes), time= 167 seconds
no anomalies in 315 test result(s)
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 330 seconds
no anomalies in 328 test result(s)
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 665 seconds
no anomalies in 344 test result(s)
rng=RNG_stdin, seed=unknown
length= 128 gigabytes (2^37 bytes), time= 1329 seconds
no anomalies in 359 test result(s)
rng=RNG_stdin, seed=unknown
length= 256 gigabytes (2^38 bytes), time= 2630 seconds
no anomalies in 372 test result(s)
rng=RNG_stdin, seed=unknown
length= 512 gigabytes (2^39 bytes), time= 6846 seconds
no anomalies in 387 test result(s)
rng=RNG_stdin, seed=unknown
length= 1 terabyte (2^40 bytes), time= 18633 seconds
no anomalies in 401 test result(s)
rng=RNG_stdin, seed=unknown
length= 2 terabytes (2^41 bytes), time= 39061 seconds
no anomalies in 413 test result(s)
```
**The tests adopted from the `xoshiro` code base**:
```txt
# ./tmp/random m128
mix3 extreme = 1.90403 (sig = 00020000) weight 1 (16), p-value = 0.608
mix3 extreme = 2.78422 (sig = 01000001) weight 2 (112), p-value = 0.453
mix3 extreme = 2.98802 (sig = 00001220) weight 3 (448), p-value = 0.716
mix3 extreme = 3.51263 (sig = 22110000) weight 4 (1120), p-value = 0.392
mix3 extreme = 3.86128 (sig = 22110200) weight >=5 (4864), p-value = 0.422
bits per word = 64 (analyzing bits); min category p-value = 0.392
processed 1.11e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.917
------
mix3 extreme = 2.33512 (sig = 00020000) weight 1 (16), p-value = 0.271
mix3 extreme = 2.60631 (sig = 00000011) weight 2 (112), p-value = 0.643
mix3 extreme = 3.20941 (sig = 00001220) weight 3 (448), p-value = 0.449
mix3 extreme = 3.23491 (sig = 10000221) weight 4 (1120), p-value = 0.744
mix3 extreme = 3.98181 (sig = 12101200) weight >=5 (4864), p-value = 0.283
bits per word = 64 (analyzing bits); min category p-value = 0.271
processed 1.29e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.794
------
mix3 extreme = 2.09795 (sig = 00020000) weight 1 (16), p-value = 0.443
mix3 extreme = 2.59859 (sig = 00000011) weight 2 (112), p-value = 0.651
mix3 extreme = 3.16919 (sig = 00020012) weight 3 (448), p-value = 0.496
mix3 extreme = 3.32522 (sig = 01002210) weight 4 (1120), p-value = 0.628
mix3 extreme = 3.87995 (sig = 20211102) weight >=5 (4864), p-value = 0.398
bits per word = 64 (analyzing bits); min category p-value = 0.398
processed 1.66e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.921
------
mix3 extreme = 1.97959 (sig = 00020000) weight 1 (16), p-value = 0.543
mix3 extreme = 2.47936 (sig = 00000011) weight 2 (112), p-value = 0.773
mix3 extreme = 3.25629 (sig = 02000022) weight 3 (448), p-value = 0.397
mix3 extreme = 3.20819 (sig = 20001012) weight 4 (1120), p-value = 0.776
mix3 extreme = 3.85604 (sig = 21111101) weight >=5 (4864), p-value = 0.429
bits per word = 64 (analyzing bits); min category p-value = 0.397
processed 1.85e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.92
------
mix3 extreme = 2.14920 (sig = 00020000) weight 1 (16), p-value = 0.402
mix3 extreme = 2.58549 (sig = 00000011) weight 2 (112), p-value = 0.665
mix3 extreme = 2.97421 (sig = 02000022) weight 3 (448), p-value = 0.732
mix3 extreme = 2.92783 (sig = 00112001) weight 4 (1120), p-value = 0.978
mix3 extreme = 3.95789 (sig = 22110200) weight >=5 (4864), p-value = 0.308
bits per word = 64 (analyzing bits); min category p-value = 0.308
processed 2.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.841
------
mix3 extreme = 1.47529 (sig = 00001000) weight 1 (16), p-value = 0.911
mix3 extreme = 2.73432 (sig = 10000100) weight 2 (112), p-value = 0.505
mix3 extreme = 2.85412 (sig = 00220100) weight 3 (448), p-value = 0.856
mix3 extreme = 3.71866 (sig = 20200011) weight 4 (1120), p-value = 0.201
mix3 extreme = 3.82703 (sig = 12212021) weight >=5 (4864), p-value = 0.468
bits per word = 64 (analyzing bits); min category p-value = 0.201
processed 2.59e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.674
------
mix3 extreme = 1.48360 (sig = 20000000) weight 1 (16), p-value = 0.907
mix3 extreme = 2.46121 (sig = 00002002) weight 2 (112), p-value = 0.79
mix3 extreme = 2.87026 (sig = 20000201) weight 3 (448), p-value = 0.841
mix3 extreme = 3.86069 (sig = 20200011) weight 4 (1120), p-value = 0.119
mix3 extreme = 3.76155 (sig = 21201200) weight >=5 (4864), p-value = 0.56
bits per word = 64 (analyzing bits); min category p-value = 0.119
processed 3.14e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.469
------
mix3 extreme = 1.84311 (sig = 20000000) weight 1 (16), p-value = 0.661
mix3 extreme = 2.57296 (sig = 10001000) weight 2 (112), p-value = 0.679
mix3 extreme = 3.19014 (sig = 20000201) weight 3 (448), p-value = 0.471
mix3 extreme = 3.98985 (sig = 20200011) weight 4 (1120), p-value = 0.0714
mix3 extreme = 3.85549 (sig = 22121010) weight >=5 (4864), p-value = 0.43
bits per word = 64 (analyzing bits); min category p-value = 0.0714
processed 4.07e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.309
------
mix3 extreme = 1.41079 (sig = 20000000) weight 1 (16), p-value = 0.937
mix3 extreme = 3.04738 (sig = 10001000) weight 2 (112), p-value = 0.228
mix3 extreme = 3.74043 (sig = 00220100) weight 3 (448), p-value = 0.079
mix3 extreme = 3.82980 (sig = 00101210) weight 4 (1120), p-value = 0.134
mix3 extreme = 3.95613 (sig = 12012120) weight >=5 (4864), p-value = 0.31
bits per word = 64 (analyzing bits); min category p-value = 0.079
processed 5.18e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.337
------
mix3 extreme = 1.46023 (sig = 00020000) weight 1 (16), p-value = 0.917
mix3 extreme = 2.75344 (sig = 00200100) weight 2 (112), p-value = 0.484
mix3 extreme = 3.35270 (sig = 20000201) weight 3 (448), p-value = 0.301
mix3 extreme = 3.74339 (sig = 02102100) weight 4 (1120), p-value = 0.184
mix3 extreme = 3.75002 (sig = 12012120) weight >=5 (4864), p-value = 0.577
bits per word = 64 (analyzing bits); min category p-value = 0.184
processed 6.1e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.638
------
mix3 extreme = 1.50237 (sig = 00000020) weight 1 (16), p-value = 0.898
mix3 extreme = 2.42220 (sig = 00200100) weight 2 (112), p-value = 0.825
mix3 extreme = 2.96916 (sig = 20000201) weight 3 (448), p-value = 0.738
mix3 extreme = 3.78954 (sig = 02102100) weight 4 (1120), p-value = 0.156
mix3 extreme = 3.66917 (sig = 10010121) weight >=5 (4864), p-value = 0.694
bits per word = 64 (analyzing bits); min category p-value = 0.156
processed 7.03e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.571
------
mix3 extreme = 1.78774 (sig = 00000020) weight 1 (16), p-value = 0.707
mix3 extreme = 2.70149 (sig = 00010100) weight 2 (112), p-value = 0.54
mix3 extreme = 3.27303 (sig = 01120000) weight 3 (448), p-value = 0.379
mix3 extreme = 4.21573 (sig = 02102100) weight 4 (1120), p-value = 0.0275
mix3 extreme = 3.70578 (sig = 12100210) weight >=5 (4864), p-value = 0.641
bits per word = 64 (analyzing bits); min category p-value = 0.0275
processed 8.5e+08 bytes in 0 seconds (inf GB/s, inf TB/h). Fri Feb 21 23:22:45 2025
p = 0.13
------
mix3 extreme = 2.31948 (sig = 10000000) weight 1 (16), p-value = 0.281
mix3 extreme = 2.44934 (sig = 00010100) weight 2 (112), p-value = 0.801
mix3 extreme = 3.53130 (sig = 01120000) weight 3 (448), p-value = 0.169
mix3 extreme = 4.28717 (sig = 02102100) weight 4 (1120), p-value = 0.0201
mix3 extreme = 3.66277 (sig = 12100210) weight >=5 (4864), p-value = 0.703
bits per word = 64 (analyzing bits); min category p-value = 0.0201
processed 1.02e+09 bytes in 1 seconds (1.017 GB/s, 3.661 TB/h). Fri Feb 21 23:22:46 2025
p = 0.0964
------
mix3 extreme = 2.60876 (sig = 00000020) weight 1 (16), p-value = 0.136
mix3 extreme = 2.67150 (sig = 22000000) weight 2 (112), p-value = 0.572
mix3 extreme = 3.39462 (sig = 01120000) weight 3 (448), p-value = 0.265
mix3 extreme = 3.62549 (sig = 02200120) weight 4 (1120), p-value = 0.276
mix3 extreme = 3.63707 (sig = 12100021) weight >=5 (4864), p-value = 0.739
bits per word = 64 (analyzing bits); min category p-value = 0.136
processed 1.26e+09 bytes in 1 seconds (1.257 GB/s, 4.526 TB/h). Fri Feb 21 23:22:46 2025
p = 0.518
------
mix3 extreme = 2.15590 (sig = 00000020) weight 1 (16), p-value = 0.397
mix3 extreme = 2.80570 (sig = 00001010) weight 2 (112), p-value = 0.431
mix3 extreme = 3.40481 (sig = 02020001) weight 3 (448), p-value = 0.257
mix3 extreme = 3.36693 (sig = 01100012) weight 4 (1120), p-value = 0.573
mix3 extreme = 3.51384 (sig = 12012120) weight >=5 (4864), p-value = 0.883
bits per word = 64 (analyzing bits); min category p-value = 0.257
processed 1.52e+09 bytes in 1 seconds (1.516 GB/s, 5.458 TB/h). Fri Feb 21 23:22:46 2025
p = 0.773
------
mix3 extreme = 1.90047 (sig = 00000020) weight 1 (16), p-value = 0.611
mix3 extreme = 3.23402 (sig = 00001010) weight 2 (112), p-value = 0.128
mix3 extreme = 2.96881 (sig = 20001001) weight 3 (448), p-value = 0.739
mix3 extreme = 3.52470 (sig = 01012002) weight 4 (1120), p-value = 0.378
mix3 extreme = 3.72569 (sig = 22111101) weight >=5 (4864), p-value = 0.612
bits per word = 64 (analyzing bits); min category p-value = 0.128
processed 1.76e+09 bytes in 1 seconds (1.756 GB/s, 6.323 TB/h). Fri Feb 21 23:22:46 2025
p = 0.495
------
mix3 extreme = 1.57279 (sig = 00000010) weight 1 (16), p-value = 0.86
mix3 extreme = 2.81387 (sig = 00001010) weight 2 (112), p-value = 0.423
mix3 extreme = 3.18715 (sig = 02002010) weight 3 (448), p-value = 0.475
mix3 extreme = 3.33070 (sig = 00101120) weight 4 (1120), p-value = 0.621
mix3 extreme = 4.16820 (sig = 20122202) weight >=5 (4864), p-value = 0.139
bits per word = 64 (analyzing bits); min category p-value = 0.139
processed 2.02e+09 bytes in 1 seconds (2.015 GB/s, 7.255 TB/h). Fri Feb 21 23:22:46 2025
p = 0.526
------
mix3 extreme = 1.53157 (sig = 00000020) weight 1 (16), p-value = 0.883
mix3 extreme = 2.71599 (sig = 00001010) weight 2 (112), p-value = 0.524
mix3 extreme = 3.16021 (sig = 02002010) weight 3 (448), p-value = 0.507
mix3 extreme = 3.26824 (sig = 00101120) weight 4 (1120), p-value = 0.703
mix3 extreme = 4.01251 (sig = 22021002) weight >=5 (4864), p-value = 0.253
bits per word = 64 (analyzing bits); min category p-value = 0.253
processed 2.51e+09 bytes in 1 seconds (2.514 GB/s, 9.052 TB/h). Fri Feb 21 23:22:46 2025
p = 0.768
------
mix3 extreme = 1.74122 (sig = 00000020) weight 1 (16), p-value = 0.744
mix3 extreme = 2.86200 (sig = 00010100) weight 2 (112), p-value = 0.377
mix3 extreme = 3.41797 (sig = 02002010) weight 3 (448), p-value = 0.246
mix3 extreme = 3.48775 (sig = 00101120) weight 4 (1120), p-value = 0.421
mix3 extreme = 4.32378 (sig = 22021002) weight >=5 (4864), p-value = 0.0719
bits per word = 64 (analyzing bits); min category p-value = 0.0719
processed 3.01e+09 bytes in 1 seconds (3.014 GB/s, 10.85 TB/h). Fri Feb 21 23:22:46 2025
p = 0.311
------
mix3 extreme = 1.61472 (sig = 00000020) weight 1 (16), p-value = 0.835
mix3 extreme = 3.22143 (sig = 00010100) weight 2 (112), p-value = 0.133
mix3 extreme = 3.34281 (sig = 02002010) weight 3 (448), p-value = 0.31
mix3 extreme = 3.41424 (sig = 10011100) weight 4 (1120), p-value = 0.512
mix3 extreme = 3.68273 (sig = 00122011) weight >=5 (4864), p-value = 0.675
bits per word = 64 (analyzing bits); min category p-value = 0.133
processed 4.01e+09 bytes in 1 seconds (4.012 GB/s, 14.44 TB/h). Fri Feb 21 23:22:46 2025
p = 0.511
------
mix3 extreme = 2.08971 (sig = 00000020) weight 1 (16), p-value = 0.45
mix3 extreme = 3.03226 (sig = 00010100) weight 2 (112), p-value = 0.238
mix3 extreme = 3.22814 (sig = 00010102) weight 3 (448), p-value = 0.428
mix3 extreme = 3.27577 (sig = 10011100) weight 4 (1120), p-value = 0.693
mix3 extreme = 3.67641 (sig = 12012120) weight >=5 (4864), p-value = 0.684
bits per word = 64 (analyzing bits); min category p-value = 0.238
processed 5.01e+09 bytes in 2 seconds (2.505 GB/s, 9.019 TB/h). Fri Feb 21 23:22:47 2025
p = 0.744
------
mix3 extreme = 1.91894 (sig = 10000000) weight 1 (16), p-value = 0.595
mix3 extreme = 2.77625 (sig = 00010100) weight 2 (112), p-value = 0.461
mix3 extreme = 3.06382 (sig = 00011001) weight 3 (448), p-value = 0.625
mix3 extreme = 3.14390 (sig = 21020200) weight 4 (1120), p-value = 0.846
mix3 extreme = 3.59615 (sig = 00122011) weight >=5 (4864), p-value = 0.792
bits per word = 64 (analyzing bits); min category p-value = 0.461
processed 6.01e+09 bytes in 2 seconds (3.004 GB/s, 10.82 TB/h). Fri Feb 21 23:22:47 2025
p = 0.954
------
mix3 extreme = 1.86597 (sig = 00000020) weight 1 (16), p-value = 0.641
mix3 extreme = 2.61078 (sig = 00010100) weight 2 (112), p-value = 0.638
mix3 extreme = 3.42404 (sig = 00011001) weight 3 (448), p-value = 0.242
mix3 extreme = 3.40741 (sig = 00020122) weight 4 (1120), p-value = 0.52
mix3 extreme = 3.89941 (sig = 00122011) weight >=5 (4864), p-value = 0.374
bits per word = 64 (analyzing bits); min category p-value = 0.242
processed 7.01e+09 bytes in 2 seconds (3.504 GB/s, 12.61 TB/h). Fri Feb 21 23:22:47 2025
p = 0.749
------
mix3 extreme = 1.73399 (sig = 00000020) weight 1 (16), p-value = 0.75
mix3 extreme = 2.96016 (sig = 20000200) weight 2 (112), p-value = 0.292
mix3 extreme = 3.09543 (sig = 02000022) weight 3 (448), p-value = 0.586
mix3 extreme = 3.42588 (sig = 10101001) weight 4 (1120), p-value = 0.497
mix3 extreme = 3.66808 (sig = 10110201) weight >=5 (4864), p-value = 0.695
bits per word = 64 (analyzing bits); min category p-value = 0.292
processed 8.5e+09 bytes in 3 seconds (2.835 GB/s, 10.21 TB/h). Fri Feb 21 23:22:48 2025
p = 0.822
------
mix3 extreme = 1.52914 (sig = 00200000) weight 1 (16), p-value = 0.885
mix3 extreme = 3.81757 (sig = 20000200) weight 2 (112), p-value = 0.015
mix3 extreme = 3.07953 (sig = 02201000) weight 3 (448), p-value = 0.605
mix3 extreme = 3.26619 (sig = 01102001) weight 4 (1120), p-value = 0.705
mix3 extreme = 3.73963 (sig = 02122020) weight >=5 (4864), p-value = 0.592
bits per word = 64 (analyzing bits); min category p-value = 0.015
processed 1e+10 bytes in 3 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:48 2025
p = 0.0727
------
mix3 extreme = 2.00898 (sig = 00020000) weight 1 (16), p-value = 0.518
mix3 extreme = 2.73872 (sig = 20000200) weight 2 (112), p-value = 0.5
mix3 extreme = 2.89865 (sig = 22000200) weight 3 (448), p-value = 0.814
mix3 extreme = 3.51139 (sig = 10002220) weight 4 (1120), p-value = 0.393
mix3 extreme = 3.46747 (sig = 21020120) weight >=5 (4864), p-value = 0.922
bits per word = 64 (analyzing bits); min category p-value = 0.393
processed 1.25e+10 bytes in 4 seconds (3.129 GB/s, 11.27 TB/h). Fri Feb 21 23:22:49 2025
p = 0.918
------
mix3 extreme = 2.54743 (sig = 00020000) weight 1 (16), p-value = 0.16
mix3 extreme = 2.66976 (sig = 00020200) weight 2 (112), p-value = 0.574
mix3 extreme = 3.21302 (sig = 02020010) weight 3 (448), p-value = 0.445
mix3 extreme = 3.17616 (sig = 10002220) weight 4 (1120), p-value = 0.812
mix3 extreme = 4.08852 (sig = 00122011) weight >=5 (4864), p-value = 0.19
bits per word = 64 (analyzing bits); min category p-value = 0.16
processed 1.5e+10 bytes in 5 seconds (3.003 GB/s, 10.81 TB/h). Fri Feb 21 23:22:50 2025
p = 0.582
------
mix3 extreme = 2.32339 (sig = 00020000) weight 1 (16), p-value = 0.278
mix3 extreme = 2.41362 (sig = 00001010) weight 2 (112), p-value = 0.832
mix3 extreme = 3.50336 (sig = 02020010) weight 3 (448), p-value = 0.186
mix3 extreme = 3.13576 (sig = 20221000) weight 4 (1120), p-value = 0.854
mix3 extreme = 4.28795 (sig = 00122011) weight >=5 (4864), p-value = 0.084
bits per word = 64 (analyzing bits); min category p-value = 0.084
processed 1.75e+10 bytes in 5 seconds (3.502 GB/s, 12.61 TB/h). Fri Feb 21 23:22:50 2025
p = 0.355
------
mix3 extreme = 2.55481 (sig = 00020000) weight 1 (16), p-value = 0.157
mix3 extreme = 2.72892 (sig = 00001010) weight 2 (112), p-value = 0.51
mix3 extreme = 3.28466 (sig = 11000001) weight 3 (448), p-value = 0.367
mix3 extreme = 3.42088 (sig = 01020102) weight 4 (1120), p-value = 0.503
mix3 extreme = 4.05525 (sig = 00122011) weight >=5 (4864), p-value = 0.216
bits per word = 64 (analyzing bits); min category p-value = 0.157
processed 2e+10 bytes in 6 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:51 2025
p = 0.575
------
mix3 extreme = 2.46488 (sig = 00020000) weight 1 (16), p-value = 0.198
mix3 extreme = 2.44365 (sig = 20000200) weight 2 (112), p-value = 0.806
mix3 extreme = 3.31277 (sig = 02020010) weight 3 (448), p-value = 0.339
mix3 extreme = 3.81631 (sig = 01020102) weight 4 (1120), p-value = 0.141
mix3 extreme = 4.12544 (sig = 00122011) weight >=5 (4864), p-value = 0.165
bits per word = 64 (analyzing bits); min category p-value = 0.141
processed 2.5e+10 bytes in 8 seconds (3.127 GB/s, 11.26 TB/h). Fri Feb 21 23:22:53 2025
p = 0.532
------
mix3 extreme = 2.18715 (sig = 00020000) weight 1 (16), p-value = 0.373
mix3 extreme = 2.10006 (sig = 20000020) weight 2 (112), p-value = 0.983
mix3 extreme = 3.08063 (sig = 00210002) weight 3 (448), p-value = 0.604
mix3 extreme = 3.66924 (sig = 02110010) weight 4 (1120), p-value = 0.239
mix3 extreme = 4.35154 (sig = 00122011) weight >=5 (4864), p-value = 0.0636
bits per word = 64 (analyzing bits); min category p-value = 0.0636
processed 3e+10 bytes in 9 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:54 2025
p = 0.28
------
mix3 extreme = 1.43572 (sig = 00020000) weight 1 (16), p-value = 0.927
mix3 extreme = 2.41760 (sig = 02000020) weight 2 (112), p-value = 0.829
mix3 extreme = 3.16026 (sig = 02100020) weight 3 (448), p-value = 0.507
mix3 extreme = 3.44650 (sig = 02220002) weight 4 (1120), p-value = 0.471
mix3 extreme = 4.35459 (sig = 00221220) weight >=5 (4864), p-value = 0.0628
bits per word = 64 (analyzing bits); min category p-value = 0.0628
processed 4e+10 bytes in 12 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:22:57 2025
p = 0.277
------
mix3 extreme = 1.73103 (sig = 02000000) weight 1 (16), p-value = 0.752
mix3 extreme = 2.59948 (sig = 02000020) weight 2 (112), p-value = 0.65
mix3 extreme = 3.24793 (sig = 02020010) weight 3 (448), p-value = 0.406
mix3 extreme = 3.18550 (sig = 00022021) weight 4 (1120), p-value = 0.802
mix3 extreme = 4.36307 (sig = 00122011) weight >=5 (4864), p-value = 0.0605
bits per word = 64 (analyzing bits); min category p-value = 0.0605
processed 5e+10 bytes in 15 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:00 2025
p = 0.268
------
mix3 extreme = 1.69245 (sig = 02000000) weight 1 (16), p-value = 0.781
mix3 extreme = 2.90012 (sig = 02000020) weight 2 (112), p-value = 0.342
mix3 extreme = 3.04010 (sig = 02100020) weight 3 (448), p-value = 0.654
mix3 extreme = 3.18726 (sig = 11012000) weight 4 (1120), p-value = 0.8
mix3 extreme = 4.13605 (sig = 00122011) weight >=5 (4864), p-value = 0.158
bits per word = 64 (analyzing bits); min category p-value = 0.158
processed 6e+10 bytes in 18 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:03 2025
p = 0.577
------
mix3 extreme = 2.02538 (sig = 02000000) weight 1 (16), p-value = 0.504
mix3 extreme = 2.69836 (sig = 20000020) weight 2 (112), p-value = 0.543
mix3 extreme = 2.74898 (sig = 00200110) weight 3 (448), p-value = 0.932
mix3 extreme = 3.09844 (sig = 11012000) weight 4 (1120), p-value = 0.887
mix3 extreme = 4.04079 (sig = 20022201) weight >=5 (4864), p-value = 0.228
bits per word = 64 (analyzing bits); min category p-value = 0.228
processed 7e+10 bytes in 21 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:06 2025
p = 0.726
------
mix3 extreme = 2.00762 (sig = 00020000) weight 1 (16), p-value = 0.519
mix3 extreme = 2.67485 (sig = 20000020) weight 2 (112), p-value = 0.569
mix3 extreme = 2.98340 (sig = 00110100) weight 3 (448), p-value = 0.722
mix3 extreme = 3.30873 (sig = 12210000) weight 4 (1120), p-value = 0.65
mix3 extreme = 4.04171 (sig = 20022201) weight >=5 (4864), p-value = 0.227
bits per word = 64 (analyzing bits); min category p-value = 0.227
processed 8.5e+10 bytes in 25 seconds (3.4 GB/s, 12.24 TB/h). Fri Feb 21 23:23:10 2025
p = 0.725
------
mix3 extreme = 1.98503 (sig = 00100000) weight 1 (16), p-value = 0.538
mix3 extreme = 2.83929 (sig = 20000020) weight 2 (112), p-value = 0.398
mix3 extreme = 3.24294 (sig = 00110100) weight 3 (448), p-value = 0.412
mix3 extreme = 3.26307 (sig = 00022012) weight 4 (1120), p-value = 0.709
mix3 extreme = 3.82851 (sig = 20212120) weight >=5 (4864), p-value = 0.466
bits per word = 64 (analyzing bits); min category p-value = 0.398
processed 1e+11 bytes in 30 seconds (3.333 GB/s, 12 TB/h). Fri Feb 21 23:23:15 2025
p = 0.921
------
mix3 extreme = 1.80847 (sig = 00000001) weight 1 (16), p-value = 0.69
mix3 extreme = 2.36679 (sig = 00001020) weight 2 (112), p-value = 0.868
mix3 extreme = 3.12962 (sig = 00110100) weight 3 (448), p-value = 0.544
mix3 extreme = 3.41207 (sig = 00022012) weight 4 (1120), p-value = 0.514
mix3 extreme = 3.50238 (sig = 01120021) weight >=5 (4864), p-value = 0.894
bits per word = 64 (analyzing bits); min category p-value = 0.514
processed 1.25e+11 bytes in 37 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:23:22 2025
p = 0.973
------
mix3 extreme = 1.61617 (sig = 01000000) weight 1 (16), p-value = 0.834
mix3 extreme = 2.75953 (sig = 10002000) weight 2 (112), p-value = 0.478
mix3 extreme = 3.01768 (sig = 22000200) weight 3 (448), p-value = 0.681
mix3 extreme = 3.12351 (sig = 20120020) weight 4 (1120), p-value = 0.865
mix3 extreme = 3.88895 (sig = 01201202) weight >=5 (4864), p-value = 0.387
bits per word = 64 (analyzing bits); min category p-value = 0.387
processed 1.5e+11 bytes in 45 seconds (3.334 GB/s, 12 TB/h). Fri Feb 21 23:23:30 2025
p = 0.914
------
mix3 extreme = 1.77082 (sig = 10000000) weight 1 (16), p-value = 0.721
mix3 extreme = 3.16452 (sig = 20000020) weight 2 (112), p-value = 0.16
mix3 extreme = 3.33010 (sig = 22000200) weight 3 (448), p-value = 0.322
mix3 extreme = 3.30032 (sig = 10202001) weight 4 (1120), p-value = 0.661
mix3 extreme = 3.71439 (sig = 01201202) weight >=5 (4864), p-value = 0.629
bits per word = 64 (analyzing bits); min category p-value = 0.16
processed 1.75e+11 bytes in 52 seconds (3.366 GB/s, 12.12 TB/h). Fri Feb 21 23:23:37 2025
p = 0.581
------
mix3 extreme = 1.63286 (sig = 10000000) weight 1 (16), p-value = 0.823
mix3 extreme = 3.04031 (sig = 20000020) weight 2 (112), p-value = 0.233
mix3 extreme = 3.07386 (sig = 00020120) weight 3 (448), p-value = 0.612
mix3 extreme = 3.25670 (sig = 20200101) weight 4 (1120), p-value = 0.717
mix3 extreme = 3.86559 (sig = 02121212) weight >=5 (4864), p-value = 0.417
bits per word = 64 (analyzing bits); min category p-value = 0.233
processed 2e+11 bytes in 59 seconds (3.39 GB/s, 12.2 TB/h). Fri Feb 21 23:23:44 2025
p = 0.734
------
mix3 extreme = 1.16364 (sig = 00000010) weight 1 (16), p-value = 0.989
mix3 extreme = 3.24185 (sig = 20000020) weight 2 (112), p-value = 0.125
mix3 extreme = 3.33043 (sig = 00020120) weight 3 (448), p-value = 0.322
mix3 extreme = 3.08521 (sig = 01100011) weight 4 (1120), p-value = 0.898
mix3 extreme = 3.38515 (sig = 20222110) weight >=5 (4864), p-value = 0.969
bits per word = 64 (analyzing bits); min category p-value = 0.125
processed 2.5e+11 bytes in 74 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:23:59 2025
p = 0.486
------
mix3 extreme = 1.51793 (sig = 02000000) weight 1 (16), p-value = 0.89
mix3 extreme = 3.13632 (sig = 02000020) weight 2 (112), p-value = 0.175
mix3 extreme = 4.30962 (sig = 00020120) weight 3 (448), p-value = 0.0073
mix3 extreme = 3.03490 (sig = 01000212) weight 4 (1120), p-value = 0.933
mix3 extreme = 3.69122 (sig = 12111112) weight >=5 (4864), p-value = 0.662
bits per word = 64 (analyzing bits); min category p-value = 0.0073
processed 3e+11 bytes in 89 seconds (3.371 GB/s, 12.14 TB/h). Fri Feb 21 23:24:14 2025
p = 0.036
------
mix3 extreme = 1.90308 (sig = 00002000) weight 1 (16), p-value = 0.609
mix3 extreme = 2.65558 (sig = 00022000) weight 2 (112), p-value = 0.589
mix3 extreme = 3.65368 (sig = 00020120) weight 3 (448), p-value = 0.109
mix3 extreme = 3.49934 (sig = 01000212) weight 4 (1120), p-value = 0.407
mix3 extreme = 3.96134 (sig = 12111112) weight >=5 (4864), p-value = 0.304
bits per word = 64 (analyzing bits); min category p-value = 0.109
processed 4e+11 bytes in 119 seconds (3.361 GB/s, 12.1 TB/h). Fri Feb 21 23:24:44 2025
p = 0.44
------
mix3 extreme = 2.01103 (sig = 00000010) weight 1 (16), p-value = 0.516
mix3 extreme = 2.75092 (sig = 20000020) weight 2 (112), p-value = 0.487
mix3 extreme = 3.58533 (sig = 00020120) weight 3 (448), p-value = 0.14
mix3 extreme = 3.00752 (sig = 00101101) weight 4 (1120), p-value = 0.948
mix3 extreme = 3.78108 (sig = 20222110) weight >=5 (4864), p-value = 0.532
bits per word = 64 (analyzing bits); min category p-value = 0.14
processed 5e+11 bytes in 148 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:25:13 2025
p = 0.53
------
mix3 extreme = 2.46075 (sig = 00000010) weight 1 (16), p-value = 0.2
mix3 extreme = 3.05595 (sig = 10001000) weight 2 (112), p-value = 0.222
mix3 extreme = 3.57444 (sig = 00020120) weight 3 (448), p-value = 0.146
mix3 extreme = 3.08460 (sig = 02100120) weight 4 (1120), p-value = 0.898
mix3 extreme = 3.57242 (sig = 21020202) weight >=5 (4864), p-value = 0.821
bits per word = 64 (analyzing bits); min category p-value = 0.146
processed 6e+11 bytes in 178 seconds (3.371 GB/s, 12.14 TB/h). Fri Feb 21 23:25:43 2025
p = 0.544
------
mix3 extreme = 2.39889 (sig = 00010000) weight 1 (16), p-value = 0.233
mix3 extreme = 3.05936 (sig = 20000020) weight 2 (112), p-value = 0.22
mix3 extreme = 3.35611 (sig = 00020120) weight 3 (448), p-value = 0.298
mix3 extreme = 3.06385 (sig = 12002002) weight 4 (1120), p-value = 0.914
mix3 extreme = 3.77731 (sig = 21020202) weight >=5 (4864), p-value = 0.538
bits per word = 64 (analyzing bits); min category p-value = 0.22
processed 7e+11 bytes in 207 seconds (3.382 GB/s, 12.17 TB/h). Fri Feb 21 23:26:12 2025
p = 0.712
------
mix3 extreme = 2.44670 (sig = 00010000) weight 1 (16), p-value = 0.207
mix3 extreme = 3.17448 (sig = 00101000) weight 2 (112), p-value = 0.155
mix3 extreme = 3.94606 (sig = 00020120) weight 3 (448), p-value = 0.035
mix3 extreme = 3.39578 (sig = 12002002) weight 4 (1120), p-value = 0.535
mix3 extreme = 3.46995 (sig = 22000212) weight >=5 (4864), p-value = 0.921
bits per word = 64 (analyzing bits); min category p-value = 0.035
processed 8.5e+11 bytes in 252 seconds (3.373 GB/s, 12.14 TB/h). Fri Feb 21 23:26:57 2025
p = 0.163
------
mix3 extreme = 2.60423 (sig = 00000200) weight 1 (16), p-value = 0.138
mix3 extreme = 4.14356 (sig = 02000020) weight 2 (112), p-value = 0.00382
mix3 extreme = 3.30244 (sig = 00210010) weight 3 (448), p-value = 0.349
mix3 extreme = 2.92058 (sig = 00111100) weight 4 (1120), p-value = 0.98
mix3 extreme = 3.64298 (sig = 21221022) weight >=5 (4864), p-value = 0.73
bits per word = 64 (analyzing bits); min category p-value = 0.00382
processed 1e+12 bytes in 296 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:27:41 2025
p = 0.019
------
mix3 extreme = 2.39411 (sig = 00000200) weight 1 (16), p-value = 0.236
mix3 extreme = 3.86207 (sig = 02000020) weight 2 (112), p-value = 0.0125
mix3 extreme = 3.43732 (sig = 00210010) weight 3 (448), p-value = 0.231
mix3 extreme = 3.30612 (sig = 10010102) weight 4 (1120), p-value = 0.654
mix3 extreme = 3.69570 (sig = 02122102) weight >=5 (4864), p-value = 0.656
bits per word = 64 (analyzing bits); min category p-value = 0.0125
processed 1.25e+12 bytes in 370 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:28:55 2025
p = 0.061
------
mix3 extreme = 1.91917 (sig = 00000200) weight 1 (16), p-value = 0.595
mix3 extreme = 2.88512 (sig = 10001000) weight 2 (112), p-value = 0.355
mix3 extreme = 3.79690 (sig = 00210010) weight 3 (448), p-value = 0.0635
mix3 extreme = 3.81192 (sig = 10010102) weight 4 (1120), p-value = 0.143
mix3 extreme = 3.94746 (sig = 20221222) weight >=5 (4864), p-value = 0.319
bits per word = 64 (analyzing bits); min category p-value = 0.0635
processed 1.5e+12 bytes in 444 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:30:09 2025
p = 0.28
------
mix3 extreme = 1.91653 (sig = 00000200) weight 1 (16), p-value = 0.598
mix3 extreme = 3.17062 (sig = 02000020) weight 2 (112), p-value = 0.157
mix3 extreme = 3.50267 (sig = 00210010) weight 3 (448), p-value = 0.186
mix3 extreme = 4.12447 (sig = 10010102) weight 4 (1120), p-value = 0.0408
mix3 extreme = 4.27098 (sig = 20221222) weight >=5 (4864), p-value = 0.0903
bits per word = 64 (analyzing bits); min category p-value = 0.0408
processed 1.75e+12 bytes in 518 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:31:23 2025
p = 0.188
------
mix3 extreme = 1.81906 (sig = 00000200) weight 1 (16), p-value = 0.681
mix3 extreme = 3.06798 (sig = 10100000) weight 2 (112), p-value = 0.215
mix3 extreme = 3.71332 (sig = 02020200) weight 3 (448), p-value = 0.0876
mix3 extreme = 4.23944 (sig = 10010102) weight 4 (1120), p-value = 0.0248
mix3 extreme = 4.44624 (sig = 20221222) weight >=5 (4864), p-value = 0.0416
bits per word = 64 (analyzing bits); min category p-value = 0.0248
processed 2e+12 bytes in 592 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:32:37 2025
p = 0.118
------
mix3 extreme = 1.53717 (sig = 01000000) weight 1 (16), p-value = 0.88
mix3 extreme = 3.32377 (sig = 10100000) weight 2 (112), p-value = 0.0947
mix3 extreme = 3.12416 (sig = 02200001) weight 3 (448), p-value = 0.55
mix3 extreme = 3.46863 (sig = 02110100) weight 4 (1120), p-value = 0.443
mix3 extreme = 4.84658 (sig = 20221222) weight >=5 (4864), p-value = 0.00609
bits per word = 64 (analyzing bits); min category p-value = 0.00609
processed 2.5e+12 bytes in 740 seconds (3.378 GB/s, 12.16 TB/h). Fri Feb 21 23:35:05 2025
p = 0.0301
------
mix3 extreme = 1.61173 (sig = 00000200) weight 1 (16), p-value = 0.837
mix3 extreme = 3.37008 (sig = 10100000) weight 2 (112), p-value = 0.0807
mix3 extreme = 3.93348 (sig = 00202100) weight 3 (448), p-value = 0.0368
mix3 extreme = 3.29910 (sig = 02200021) weight 4 (1120), p-value = 0.663
mix3 extreme = 4.29826 (sig = 20221222) weight >=5 (4864), p-value = 0.0803
bits per word = 64 (analyzing bits); min category p-value = 0.0368
processed 3e+12 bytes in 887 seconds (3.382 GB/s, 12.18 TB/h). Fri Feb 21 23:37:32 2025
p = 0.171
------
mix3 extreme = 2.09163 (sig = 00000200) weight 1 (16), p-value = 0.448
mix3 extreme = 2.61382 (sig = 01100000) weight 2 (112), p-value = 0.635
mix3 extreme = 3.98155 (sig = 00202100) weight 3 (448), p-value = 0.0302
mix3 extreme = 3.38573 (sig = 00002211) weight 4 (1120), p-value = 0.549
mix3 extreme = 4.15291 (sig = 20211110) weight >=5 (4864), p-value = 0.148
bits per word = 64 (analyzing bits); min category p-value = 0.0302
processed 4e+12 bytes in 1.18e+03 seconds (3.381 GB/s, 12.17 TB/h). Fri Feb 21 23:42:28 2025
p = 0.142
------
```
------------------------------------------------------------
------------------------------------------------------------
# `./fio-stl/000 copyright.h`
1 public symbols.
### Macros
#### `FIO_INCLUDE_FILE`
```c
#define FIO_INCLUDE_FILE "fio-stl.h"
```
Persists the include file name (single file vs. folder)
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/000 core.h`
1764 public symbols.
### Definition / Code Generation Macros
#### `FIO_DEF_GET_FUNC_DEC`
```c
#define FIO_DEF_GET_FUNC_DEC(static, namespace, T_type, F_type, field_name) \
/** Returns current value of property within the struct / union. */ \
static F_type FIO_NAME(namespace, field_name)(T_type * o);
```
Declares (names) a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GET_FUNC`
```c
#define FIO_DEF_GET_FUNC(static, namespace, T_type, F_type, field_name) \
/** Returns current value of property within the struct / union. */ \
static F_type FIO_NAME(namespace, field_name)(T_type * o) { \
FIO_ASSERT_DEBUG(o, "NULL " FIO_MACRO2STR(namespace) " pointer @ `get`!"); \
return o->field_name; \
}
```
Defines a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_SET_FUNC_DEC`
```c
#define FIO_DEF_SET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
/** Sets a new value, returning the old one */ \
static F_type FIO_NAME(FIO_NAME(namespace, F_name), set)(T_type * o, \
F_type new_value);
```
Declares (names) a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_SET_FUNC`
```c
#define FIO_DEF_SET_FUNC(static, namespace, T_type, F_type, F_name, on_set) \
/** Sets a new value, returning the old one */ \
static F_type FIO_NAME(FIO_NAME(namespace, F_name), set)(T_type * o, \
F_type new_value) { \
FIO_ASSERT_DEBUG(o, "NULL " FIO_MACRO2STR(namespace) " pointer @ `set`!"); \
F_type old_value = o->F_name; \
o->F_name = new_value; \
on_set(o); \
return old_value; \
}
```
Defines a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GETSET_FUNC`
```c
#define FIO_DEF_GETSET_FUNC(static, namespace, T_type, F_type, F_name, on_set) \
FIO_DEF_GET_FUNC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_SET_FUNC(static, namespace, T_type, F_type, F_name, on_set)
```
Defines `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_DEF_GETSET_FUNC_DEC`
```c
#define FIO_DEF_GETSET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_GET_FUNC_DEC(static, namespace, T_type, F_type, F_name) \
FIO_DEF_SET_FUNC_DEC(static, namespace, T_type, F_type, F_name)
```
Declares (names) `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_GET`
```c
#define FIO_IFUNC_DEF_GET(namespace, T_type, F_type, field_name) \
FIO_DEF_GET_FUNC(FIO_IFUNC, namespace, T_type, F_type, field_name)
```
Defines a `get` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_SET`
```c
#define FIO_IFUNC_DEF_SET(namespace, T_type, F_type, F_name, on_set) \
FIO_DEF_SET_FUNC(FIO_IFUNC, namespace, T_type, F_type, F_name, on_set)
```
Defines a `set` function for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_IFUNC_DEF_GETSET`
```c
#define FIO_IFUNC_DEF_GETSET(namespace, T_type, F_type, F_name, on_set) \
FIO_IFUNC_DEF_GET(namespace, T_type, F_type, F_name) \
FIO_IFUNC_DEF_SET(namespace, T_type, F_type, F_name, on_set)
```
Defines `get/set` functions for a field within a struct / union.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_STATIC_ALLOC_DEF`
```c
#define FIO_STATIC_ALLOC_DEF(name, \
type_T, \
units_per_allocation, \
max_concurrent_allocations) \
/** Returns a slot from the `name` static arena. */ \
FIO_SFUNC FIO_WARN_UNUSED type_T *name(size_t count) { \
static type_T name##buffer[sizeof(type_T) * (max_concurrent_allocations) * \
(units_per_allocation)]; \
static size_t pos; \
if (!count) \
return name##buffer; \
size_t at = fio_atomic_add(&pos, count); \
at %= (max_concurrent_allocations); \
return (at * (units_per_allocation)) + name##buffer; \
} \
/** Returns the logical arena capacity in `type_T` units. */ \
FIO_IFUNC size_t name##_size(void) { \
return (size_t)((max_concurrent_allocations) * (units_per_allocation)); \
}
```
Defines a statically backed, round-robin allocator named `name`.
This defines the following functions:
```c
static type_T *name(size_t count);
static inline size_t name##_size(void);
```
The allocator has `max_concurrent_allocations` slots, each containing
`units_per_allocation` `type_T` elements. `name(count)` returns a pointer to
the slot selected by the atomic round-robin position. `count` advances that
position by the requested number of slots; it does not change the size of
the returned slot. Passing `0` returns the first slot without advancing.
The memory is static, correctly aligned for `type_T`, and must not be freed.
`name##_size()` returns the logical arena capacity in `type_T` units:
```c
max_concurrent_allocations * units_per_allocation
```
A returned slot remains valid until the allocator wraps and reuses it. Set
`max_concurrent_allocations` for the maximum number of outstanding slots,
including concurrent callers.
Example use:
```c
// defines a static allocator for 19-character strings
FIO_STATIC_ALLOC_DEF(numer2hex_allocator,
char,
19,
FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX);
// a function that returns an unsigned number as a 16 digit hex string
char *ntos16(uint16_t n) {
char *buf = numer2hex_allocator(1);
buf[0] = '0'; buf[1] = 'x';
fio_ltoa16u(buf + 2, n, 16);
buf[18] = 0;
return buf;
}
```
A similar approach is used by `fiobj_num2cstr` for temporary FIOBJ-to-string
conversions that do not require memory management.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_STATIC_SAFE_ALLOC_DEF`
```c
#define FIO_STATIC_SAFE_ALLOC_DEF(name, \
type_T, \
units_per_allocation, \
max_concurrent_allocations) \
enum { name##__hdr_ = (int)((sizeof(type_T) < 16) ? sizeof(type_T) : 16) }; \
typedef struct FIO_ALIGN(16) name##__slot_s { \
unsigned char busy_[name##__hdr_]; \
type_T data_[(units_per_allocation)]; \
} name##__slot_s; \
FIO_ASSERT_STATIC(offsetof(name##__slot_s, data_) == (size_t)name##__hdr_, \
"FIO_STATIC_SAFE_ALLOC_DEF: slot metadata header must be" \
" min(sizeof(type_T), 16) bytes"); \
/** Checks out a slot; returns its data block, or NULL when all are busy. */ \
FIO_SFUNC FIO_WARN_UNUSED type_T *name##_try(void) { \
static name##__slot_s name##buffer[(max_concurrent_allocations)]; \
static size_t hint; \
size_t start = fio_atomic_add(&hint, 1); \
for (size_t i = 0; i < (size_t)(max_concurrent_allocations); ++i) { \
size_t at = (start + i) % (size_t)(max_concurrent_allocations); \
if (!(fio_atomic_or(name##buffer[at].busy_, 1) & 1)) \
return name##buffer[at].data_; \
} \
return NULL; \
} \
/** Releases a slot previously returned by name##_try. */ \
FIO_SFUNC void name##_free(type_T *ptr) { \
name##__slot_s *slot = \
(name##__slot_s *)(void *)((char *)ptr - name##__hdr_); \
fio_atomic_and(slot->busy_, 0); \
} \
/** Returns the logical arena capacity in `type_T` units. */ \
FIO_IFUNC size_t name##_size(void) { \
return (size_t)((max_concurrent_allocations) * (units_per_allocation)); \
}
```
FIO_STATIC_SAFE_ALLOC_DEF(name, type_T, units_per_allocation,
max_concurrent_allocations)
A contention-safe variant of FIO_STATIC_ALLOC_DEF for short-lived scratch
slots with an explicit checkout lifecycle. Unlike the round-robin variant
(which silently reuses slots past `max_concurrent_allocations`), this
allocator returns NULL when every slot is busy, letting callers fall back
gracefully instead of corrupting an in-flight user of the slot.
Each slot carries a small metadata header holding the busy byte. The
header is `min(sizeof(type_T), 16)` bytes: one element for small types
(data naturally aligned for `type_T`), capped at 16 bytes for larger
types (data 16-byte aligned). `type_T` alignment must be <= 16 (enforced
at compile time).
Generated API:
type_T *name##_try(void); - checkout a slot, or NULL if all busy.
void name##_free(type_T *); - release a slot returned by name##_try.
size_t name##_size(void); - logical arena capacity in type_T units.
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X64_DEF`
```c
#define FIO_UXXX_X64_DEF(name, bits) \
uint64x2_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 64bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X32_DEF`
```c
#define FIO_UXXX_X32_DEF(name, bits) \
uint32x4_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 32bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X16_DEF`
```c
#define FIO_UXXX_X16_DEF(name, bits) \
uint16x8_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 16bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
#### `FIO_UXXX_X8_DEF`
```c
#define FIO_UXXX_X8_DEF(name, bits) uint8x16_t name[(bits / 128) + (bits < 128)]
```
Defines a `bits` long vector using unsigned 8bit words
_Note:_ this MACRO defines or declares code.
_Symbol type:_ `macro`
### Macros
#### `FIO_NOOP`
```c
#define FIO_NOOP
```
An empty macro, adding white space. Used to avoid function like macros.
_Symbol type:_ `macro`
#### `FIO_NOOP_FN`
```c
#define FIO_NOOP_FN(...)
```
A no-op macro that takes arguments. i.e., for use in `FIO_DEF_SET_FUNC`
_Symbol type:_ `macro`
#### `FIO_NOOP_FN_NAME`
```c
#define FIO_NOOP_FN_NAME (void)
```
Macro for a No-Op function name (void).
_Symbol type:_ `macro`
#### `FIO_VERSION_MAJOR`
```c
#define FIO_VERSION_MAJOR 0
```
MAJOR version: API/ABI breaking changes.
_Symbol type:_ `macro`
#### `FIO_VERSION_MINOR`
```c
#define FIO_VERSION_MINOR 8
```
MINOR version: Deprecation, or significant features added. May break ABI.
_Symbol type:_ `macro`
#### `FIO_VERSION_PATCH`
```c
#define FIO_VERSION_PATCH 0
```
PATCH version: Bug fixes, minor features may be added.
_Symbol type:_ `macro`
#### `FIO_VERSION_STRING`
```c
#define FIO_VERSION_STRING \
FIO_MACRO2STR(FIO_VERSION_MAJOR) \
"." FIO_MACRO2STR(FIO_VERSION_MINOR) "." FIO_MACRO2STR( \
FIO_VERSION_PATCH) "-" FIO_VERSION_BUILD
```
Version as a String literal (MACRO).
_Symbol type:_ `macro`
#### `FIO_VERSION_BUILD`
```c
#define FIO_VERSION_BUILD ""
```
_Symbol type:_ `macro`
#### `FIO_LEAK_COUNTER`
```c
#define FIO_LEAK_COUNTER 1
```
Enables memory leak detection. Disable by setting this to zero.
_Symbol type:_ `macro`
#### `FIO_USE_THREAD_MUTEX`
```c
#define FIO_USE_THREAD_MUTEX 0
```
Selects between facio.io's spinlocks (false) and OS mutexes (true)
_Symbol type:_ `macro`
#### `FIO_UNALIGNED_ACCESS`
```c
#define FIO_UNALIGNED_ACCESS 1
```
Allows facil.io to attempt unaligned memory access on *some* CPU systems.
_Symbol type:_ `macro`
#### `FIO_LIMIT_INTRINSIC_BUFFER`
```c
#define FIO_LIMIT_INTRINSIC_BUFFER 1
```
Limits register consumption on some pseudo-intrinsics, using more loops
_Symbol type:_ `macro`
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT 1
```
Memory allocations should be secure by default (facil.io allocators only)
_Symbol type:_ `macro`
#### `FIO_MEM_PAGE_SIZE_LOG`
```c
#define FIO_MEM_PAGE_SIZE_LOG 12 /* assumes 4096 bytes per page */
```
Compile-time memory page size log (12 == 4096 bytes).
_Symbol type:_ `macro`
#### `FIO_MAP_WARNING_BITSIZE`
```c
#define FIO_MAP_WARNING_BITSIZE ((size_t)24)
```
iMap and Map allocation size warning (log2), 24 == 16Mb.
_Symbol type:_ `macro`
#### `FIO_ALIGN`
```c
#define FIO_ALIGN(bytes) __attribute__((aligned(bytes)))
```
Sets alignment is supported by compiler.
_Symbol type:_ `macro`
#### `FIO_COMPILER_GUARD`
```c
#define FIO_COMPILER_GUARD __asm__ volatile("" ::: "memory")
```
Clobber CPU registers and prevent compiler reordering optimizations.
_Symbol type:_ `macro`
#### `FIO_COMPILER_GUARD_INSTRUCTION`
```c
#define FIO_COMPILER_GUARD_INSTRUCTION __asm__ volatile("" :::)
```
Prevent compiler reordering optimizations.
_Symbol type:_ `macro`
#### `FIO_UNALIGNED_MEMORY_ACCESS_ENABLED`
```c
#define FIO_UNALIGNED_MEMORY_ACCESS_ENABLED 1
```
True when unaligned memory is allowed.
_Symbol type:_ `macro`
#### `FIO_OS_POSIX`
```c
#define FIO_OS_POSIX 0
```
Always defined, true on POSIX systems.
_Symbol type:_ `macro`
#### `FIO_OS_WIN`
```c
#define FIO_OS_WIN 0
```
Always defined, true on Windows systems.
_Symbol type:_ `macro`
#### `FIO_KILL_SELF`
```c
#define FIO_KILL_SELF() GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)
```
Sends a signal to kill the current process (emulates CTRL + C).
_Symbol type:_ `macro`
#### `FIO_HAVE_UNIX_TOOLS`
```c
#define FIO_HAVE_UNIX_TOOLS 2
```
True if POSIX headers are available.
_Symbol type:_ `macro`
#### `FIO_SIZE_T_ERROR`
```c
#define FIO_SIZE_T_ERROR (~(size_t)0)
```
The `-1` value for a `size_t` type, often used as an error value.
_Symbol type:_ `macro`
#### `FIO_MAYBE_UNUSED`
```c
#define FIO_MAYBE_UNUSED __attribute__((unused))
```
Marks a variable / function as possibly unused (if compiler supported).
_Symbol type:_ `macro`
#### `FIO_SFUNC`
```c
#define FIO_SFUNC static FIO_MAYBE_UNUSED
```
static, possibly unused, function.
_Symbol type:_ `macro`
#### `FIO_IFUNC`
```c
#define FIO_IFUNC FIO_SFUNC inline
```
Marks a function as `static`, `inline` and possibly unused.
_Symbol type:_ `macro`
#### `FIO_WARN_UNUSED`
```c
#define FIO_WARN_UNUSED __attribute__((warn_unused_result))
```
Attribute for functions whose return value should not be ignored.
_Symbol type:_ `macro`
#### `FIO_MIFN`
```c
#define FIO_MIFN FIO_IFUNC FIO_WARN_UNUSED
```
Marks a function as inline with warn_unused_result (for math functions).
_Symbol type:_ `macro`
#### `FIO_CONST`
```c
#define FIO_CONST __attribute__((const))
```
Marks a function as const (no side effects, result depends only on args)
_Symbol type:_ `macro`
#### `FIO_PURE`
```c
#define FIO_PURE __attribute__((pure))
```
Marks a function as pure (no side effects, may read global memory)
_Symbol type:_ `macro`
#### `FIO_WEAK`
```c
#define FIO_WEAK __attribute__((weak))
```
Marks a function as weak
_Symbol type:_ `macro`
#### `FIO_WEAK_VAR`
```c
#define FIO_WEAK_VAR __declspec(selectany)
```
Marks a global variable as weak
_Symbol type:_ `macro`
#### `FIO_CONSTRUCTOR`
```c
#define FIO_CONSTRUCTOR(fname) \
static __attribute__((constructor)) void fname(void)
```
Marks a function as a constructor - if supported.
_Symbol type:_ `macro`
#### `FIO_DESTRUCTOR`
```c
#define FIO_DESTRUCTOR(fname) \
static __attribute__((destructor)) void fname(void)
```
Marks a function as a destructor - if supported. Consider using atexit()
_Symbol type:_ `macro`
#### `FIO_LIKELY`
```c
#define FIO_LIKELY(cond) __builtin_expect((cond), 1)
```
Provides a compiler hint for value likelihood, if possible.
_Symbol type:_ `macro`
#### `FIO_UNLIKELY`
```c
#define FIO_UNLIKELY(cond) __builtin_expect((cond), 0)
```
Provides a compiler hint for value likelihood, if possible.
_Symbol type:_ `macro`
#### `FIO_PREFETCH`
```c
#define FIO_PREFETCH(ptr) __builtin_prefetch((ptr), 0, 3)
```
Prefetch data for reading into all cache levels.
_Symbol type:_ `macro`
#### `FIO_PREFETCH_W`
```c
#define FIO_PREFETCH_W(ptr) __builtin_prefetch((ptr), 1, 3)
```
Prefetch data for writing (exclusive cache line ownership).
_Symbol type:_ `macro`
#### `FIO_PREFETCH_NT`
```c
#define FIO_PREFETCH_NT(ptr) __builtin_prefetch((ptr), 0, 0)
```
Prefetch non-temporal (streaming data, minimize cache pollution).
_Symbol type:_ `macro`
#### `FIO_MACRO2STR`
```c
#define FIO_MACRO2STR(macro) FIO___MACRO2STR_STEP2(macro)
```
Converts a macro's content to a string literal.
_Symbol type:_ `macro`
#### `FIO_NAME`
```c
#define FIO_NAME(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, _)
```
Used for naming functions and variables resulting in: prefix_postfix
_Symbol type:_ `macro`
#### `FIO_NAME2`
```c
#define FIO_NAME2(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, 2)
```
Sets naming convention for conversion functions, i.e.: foo2bar
_Symbol type:_ `macro`
#### `FIO_NAME_BL`
```c
#define FIO_NAME_BL(prefix, postfix) \
FIO___NAME_FROM_MACRO_STEP1(prefix, postfix, _is_)
```
Sets naming convention for boolean testing functions, i.e.: foo_is_true
_Symbol type:_ `macro`
#### `FIO_NAME_TEST`
```c
#define FIO_NAME_TEST(prefix, postfix) \
FIO_NAME(fio___test, FIO_NAME(prefix, postfix))
```
Used internally to name test functions.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_LMASK`
```c
#define FIO_PTR_MATH_LMASK(T_type, ptr, bits) \
((T_type *)(((uintptr_t)(ptr)) & (((uintptr_t)1ULL << (bits)) - 1)))
```
Masks a pointer's left-most bits, returning the right bits.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_RMASK`
```c
#define FIO_PTR_MATH_RMASK(T_type, ptr, bits) \
((T_type *)(((uintptr_t)(ptr)) & ((~(uintptr_t)0ULL) << (bits))))
```
Masks a pointer's right-most bits, returning the left bits.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_ADD`
```c
#define FIO_PTR_MATH_ADD(T_type, ptr, offset) \
((T_type *)((uintptr_t)(ptr) + (uintptr_t)(offset)))
```
Add offset bytes to pointer, updating the pointer's type.
_Symbol type:_ `macro`
#### `FIO_PTR_MATH_SUB`
```c
#define FIO_PTR_MATH_SUB(T_type, ptr, offset) \
((T_type *)((uintptr_t)(ptr) - (uintptr_t)(offset)))
```
Subtract X bytes from pointer, updating the pointer's type.
_Symbol type:_ `macro`
#### `FIO_PTR_FIELD_OFFSET`
```c
#define FIO_PTR_FIELD_OFFSET(T_type, field) \
((uintptr_t)((&((T_type *)0xFF00)->field)) - 0xFF00)
```
Find the root object (of a struct) from it's field (with sanitizer fix).
_Symbol type:_ `macro`
#### `FIO_PTR_FROM_FIELD`
```c
#define FIO_PTR_FROM_FIELD(T_type, field, ptr) \
FIO_PTR_MATH_SUB(T_type, ptr, FIO_PTR_FIELD_OFFSET(T_type, field))
```
Find the root object (of a struct) from it's field (with sanitizer fix).
_Symbol type:_ `macro`
#### `FIO_THREAD_WAIT`
```c
#define FIO_THREAD_WAIT(nano_sec) \
do { \
const struct timespec tm = {.tv_sec = (time_t)((nano_sec) / 1000000000), \
.tv_nsec = ((long)(nano_sec) % 1000000000)}; \
nanosleep(&tm, (struct timespec *)NULL); \
} while (0)
```
Calls nanonsleep with the requested nano-second count.
_Symbol type:_ `macro`
#### `FIO_THREAD_YIELD`
```c
#define FIO_THREAD_YIELD() __asm__ __volatile__("pause" ::: "memory")
```
Yields the thread, hinting to the processor about spinlock loop.
_Symbol type:_ `macro`
#### `FIO_THREAD_RESCHEDULE`
```c
#define FIO_THREAD_RESCHEDULE() FIO_THREAD_WAIT(4)
```
Reschedules the thread by calling nanosleeps for nano-seconds.
In practice, the thread will probably sleep for 60ns or more.
Seems to be faster then thread_yield, perhaps it prevents de-prioritization
of the thread.
_Symbol type:_ `macro`
#### `FIO_LOCK_INIT`
```c
#define FIO_LOCK_INIT 0
```
Initialization value for a spin lock.
_Symbol type:_ `macro`
#### `FIO_LOCK_SUBLOCK`
```c
#define FIO_LOCK_SUBLOCK(sub) ((uint8_t)(1U) << ((sub)&7))
```
A numbered sub-lock index, must be less than 8. Zero == the master lock.
_Symbol type:_ `macro`
#### `FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX`
```c
#define FIO_STATIC_ALLOC_SAFE_CONCURRENCY_MAX 256
```
Default slot count used by selected internal static allocators.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_NONE`
```c
#define FIO_LOG_LEVEL_NONE 0
```
Logging level of zero (no logging).
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_FATAL`
```c
#define FIO_LOG_LEVEL_FATAL 1
```
Log fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_ERROR`
```c
#define FIO_LOG_LEVEL_ERROR 2
```
Log errors and fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_WARNING`
```c
#define FIO_LOG_LEVEL_WARNING 3
```
Log warnings, errors and fatal errors.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_INFO`
```c
#define FIO_LOG_LEVEL_INFO 4
```
Log every message (info, warnings, errors and fatal errors).
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_DEBUG`
```c
#define FIO_LOG_LEVEL_DEBUG 5
```
Log everything, including debug messages.
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_SET`
```c
#define FIO_LOG_LEVEL_SET(new_level) (0)
```
Sets the Logging Level
_Symbol type:_ `macro`
#### `FIO_LOG_LEVEL_GET`
```c
#define FIO_LOG_LEVEL_GET() (0)
```
Returns the Logging Level
_Symbol type:_ `macro`
#### `FIO_LOG_WRITE`
```c
#define FIO_LOG_WRITE(...) FIO_LOG2STDERR("(" FIO___FILE__ ":" FIO_MACRO2STR(__LINE__) "): " __VA_ARGS__)
```
Writes a message to the log, including the file name and line number.
_Symbol type:_ `macro`
#### `FIO_LOG_FATAL`
```c
#define FIO_LOG_FATAL(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_FATAL, "\x1B[1m\x1B[7mFATAL:\x1B[0m " __VA_ARGS__)
```
Writes a Fatal message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_ERROR`
```c
#define FIO_LOG_ERROR(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_ERROR, "\x1B[1mERROR:\x1B[0m " __VA_ARGS__)
```
Writes a Error message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_SECURITY`
```c
#define FIO_LOG_SECURITY(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_ERROR, "\x1B[1mSECURITY:\x1B[0m " __VA_ARGS__)
```
Writes a Security message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_WARNING`
```c
#define FIO_LOG_WARNING(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_WARNING, "\x1B[2mWARNING:\x1B[0m " __VA_ARGS__)
```
Writes a Warning message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_INFO`
```c
#define FIO_LOG_INFO(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_INFO, "INFO: " __VA_ARGS__)
```
Writes a Info message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_DEBUG`
```c
#define FIO_LOG_DEBUG(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_DEBUG,"DEBUG: (" FIO___FILE__ ":" FIO_MACRO2STR(__LINE__) ") " __VA_ARGS__)
```
Writes a Debug message to the log, including the file name and line number.
_Symbol type:_ `macro`
#### `FIO_LOG_DEBUG2`
```c
#define FIO_LOG_DEBUG2(...) FIO___LOG_PRINT_LEVEL(FIO_LOG_LEVEL_DEBUG, "DEBUG: " __VA_ARGS__)
```
Writes a Debug message to the log.
_Symbol type:_ `macro`
#### `FIO_LOG_DDEBUG`
```c
#define FIO_LOG_DDEBUG(...) FIO_LOG_DEBUG(__VA_ARGS__)
```
Writes a Debug message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DDEBUG2`
```c
#define FIO_LOG_DDEBUG2(...) FIO_LOG_DEBUG2(__VA_ARGS__)
```
Writes a Debug2 message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DERROR`
```c
#define FIO_LOG_DERROR(...) FIO_LOG_ERROR(__VA_ARGS__)
```
Writes a Error message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DSECURITY`
```c
#define FIO_LOG_DSECURITY(...) FIO_LOG_SECURITY(__VA_ARGS__)
```
Writes a Security message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DWARNING`
```c
#define FIO_LOG_DWARNING(...) FIO_LOG_WARNING(__VA_ARGS__)
```
Writes a Warning message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_DINFO`
```c
#define FIO_LOG_DINFO(...) FIO_LOG_INFO(__VA_ARGS__)
```
Writes a Info message to the log only if `DEBUG` was defined.
_Symbol type:_ `macro`
#### `FIO_LOG_LENGTH_LIMIT`
```c
#define FIO_LOG_LENGTH_LIMIT 1024
```
Defines a point at which logging truncates (limits stack memory use)
_Symbol type:_ `macro`
#### `FIO_LOG2STDERR`
```c
#define FIO_LOG2STDERR(...)
```
Prints to STDERR, attempting to use only stack allocated memory.
_Symbol type:_ `macro`
#### `FIO_STDERR_FILE`
```c
#define FIO_STDERR_FILE stderr
```
_Symbol type:_ `macro`
#### `FIO_ASSERT`
```c
#define FIO_ASSERT(cond, ...) \
do { \
if (FIO_UNLIKELY(!(cond))) { \
FIO_LOG_FATAL(__VA_ARGS__); \
FIO_LOG_FATAL(" errno(%d): %s\n", errno, strerror(errno)); \
fflush(FIO_STDERR_FILE); \
FIO___ASSERT_PERFORM_SIGNAL(); \
exit(-1); \
} \
} while (0)
```
Asserts a condition is true, or kills the application using SIGINT.
_Symbol type:_ `macro`
#### `FIO_ASSERT_ALLOC`
```c
#define FIO_ASSERT_ALLOC(ptr) FIO_ASSERT((ptr), "memory allocation failed.")
```
Tests for an allocation failure. The behavior can be overridden.
_Symbol type:_ `macro`
#### `FIO_ASSERT_DEBUG`
```c
#define FIO_ASSERT_DEBUG(cond, ...) \
do { \
if (!(cond)) { \
FIO_LOG_FATAL("(" FIO___FILE__ \
":" FIO_MACRO2STR(__LINE__) ") " __VA_ARGS__); \
FIO_LOG_FATAL(" errno(%d): %s\n", errno, strerror(errno)); \
fflush(FIO_STDERR_FILE); \
FIO___ASSERT_PERFORM_SIGNAL(); \
exit(-1); \
} \
} while (0)
```
If `DEBUG` is defined, raises SIGINT if assertion fails, otherwise NOOP.
_Symbol type:_ `macro`
#### `FIO_ASSERT_STATIC`
```c
#define FIO_ASSERT_STATIC(cond, msg) _Static_assert((cond), msg)
```
Perform a static (build time) assertion.
_Symbol type:_ `macro`
#### `FIO_MEM_STACK_WIPE`
```c
#define FIO_MEM_STACK_WIPE(pages) \
do { \
volatile char stack_mem[(pages) << 12] = {0}; \
(void)stack_mem; \
} while (0)
```
Places `pages` x 4096 bytes of zero (`0`) on the stack.
_Symbol type:_ `macro`
#### `FIO_MEMCPY`
```c
#define FIO_MEMCPY fio_memcpy
```
Use this macro instead of `memcpy`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMMOVE`
```c
#define FIO_MEMMOVE fio_memcpy
```
Use this macro instead of `memmove`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMCMP`
```c
#define FIO_MEMCMP fio_memcmp
```
Use this macro instead of `memcmp`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMCHR`
```c
#define FIO_MEMCHR fio_memchr
```
Use this macro instead of `memchr`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_MEMSET`
```c
#define FIO_MEMSET fio_memset
```
Use this macro instead of `memset`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_STRLEN`
```c
#define FIO_STRLEN fio_strlen
```
Use this macro instead of `strlen`, for easy implementation overriding.
_Symbol type:_ `macro`
#### `FIO_FOR`
```c
#define FIO_FOR(i, count) for (size_t i = 0; i < (count); ++i)
```
Helper for simple `for` loops, where `i` is the variable name to use.
_Symbol type:_ `macro`
#### `FIO_FOR_UNROLL_GROUP_SIZE`
```c
#define FIO_FOR_UNROLL_GROUP_SIZE ((size_t)256U)
```
The byte stride used by `FIO_FOR_UNROLL` for compiler-friendly batches.
Most CPUs should be able to hold at least 512 bytes in their registers. The
default value assumes half that is available for the optimized loop, as the
action within each loop will likely require some available registers.
_Symbol type:_ `macro`
#### `FIO_FOR_UNROLL`
```c
#define FIO_FOR_UNROLL(iterations, size_of_loop, i, action) \
do { \
size_t i = 0; \
const size_t fio___unroll_remainder__ = \
((iterations) & ((FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)) - 1)); \
/* handle odd length vectors, not multiples of FIO___LOG2V */ \
if (fio___unroll_remainder__ && ((iterations) + 1)) \
for (; i < fio___unroll_remainder__; ++i) \
action; \
if (iterations) \
for (; !((iterations) + 1) || (i < (iterations));) { \
for (size_t j__loop__ = 0; \
j__loop__ < (FIO_FOR_UNROLL_GROUP_SIZE / (size_of_loop)); \
++j__loop__, ++i) /* dear compiler, please vectorize */ \
action; \
} \
} while (0)
```
Unrolled `for` loop - separates for loops to make it easier for the compiler
to optimize.
**Parameters:**
- `iterations` - the number of loop iterations to perform
- `size_of_loop` - the number of bytes consumed by each `action`
- `i` - the loop index variable name to use (accessible by `action`)
- `action` - an action to be performed each iteration (can be a macro)
Note that `action` is NOT guarded. In the following `strlen` example, the
`if` statement will run only after each SIMD loop has ended, allowing the
compiler to unroll the loops:
```c
size_t has_zero = 0;
const size_t iterations = ((~(uintptr_t)str) + 1) | (1ULL << 31);
#define FIO___STRLEN_ACTION
has_zero += (!str[i] | !!has_zero);
if (has_zero)
return i - has_zero;
FIO_FOR_UNROLL(iterations, 1, i, FIO___STRLEN_ACTION);
#undef FIO___STRLEN_ACTION
return ~(size_t)0;
```
_Symbol type:_ `macro`
#### `FIO_LEAK_COUNTER_SKIP_EXIT`
```c
#define FIO_LEAK_COUNTER_SKIP_EXIT 0
```
If set, no `FIO_CALL_AFTER_EXIT` leak reporting event is registered.
_Symbol type:_ `macro`
#### `FIO_SHIFT_FORWARDS`
```c
#define FIO_SHIFT_FORWARDS(i, bits) ((i) >> (bits))
```
An endianess dependent shift operation, moves bits forwards.
_Symbol type:_ `macro`
#### `FIO_SHIFT_BACKWARDS`
```c
#define FIO_SHIFT_BACKWARDS(i, bits) ((i) << (bits))
```
An endianess dependent shift operation, moves bits backwards.
_Symbol type:_ `macro`
#### `FIO_LIST_NODE`
```c
#define FIO_LIST_NODE fio_list_node_s
```
A linked list node type
_Symbol type:_ `macro`
#### `FIO_LIST_HEAD`
```c
#define FIO_LIST_HEAD fio_list_node_s
```
A linked list head type
_Symbol type:_ `macro`
#### `FIO_LIST_INIT`
```c
#define FIO_LIST_INIT(obj) \
(fio_list_node_s) { .next = &(obj), .prev = &(obj) }
```
Allows initialization of FIO_LIST_HEAD objects.
_Symbol type:_ `macro`
#### `FIO_LIST_EACH`
```c
#define FIO_LIST_EACH(type, node_name, head, pos) \
for (type *pos = \
((head)->next ? FIO_PTR_FROM_FIELD(type, node_name, (head)->next) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))), \
*next____p_ls_##pos = \
((head)->next \
? FIO_PTR_FROM_FIELD(type, node_name, (head)->next->next) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.next)))
```
Loops through every node in the linked list except the head.
_Symbol type:_ `macro`
#### `FIO_LIST_EACH_REVERSED`
```c
#define FIO_LIST_EACH_REVERSED(type, node_name, head, pos) \
for (type *pos = \
((head)->prev ? FIO_PTR_FROM_FIELD(type, node_name, (head)->prev) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))), \
*next____p_ls_##pos = \
((head)->prev \
? FIO_PTR_FROM_FIELD(type, node_name, (head)->prev->prev) \
: FIO_PTR_FROM_FIELD(type, node_name, (head))); \
pos != FIO_PTR_FROM_FIELD(type, node_name, (head)); \
(pos = next____p_ls_##pos), \
(next____p_ls_##pos = \
FIO_PTR_FROM_FIELD(type, \
node_name, \
next____p_ls_##pos->node_name.prev)))
```
Loops through every node in the linked list except the head.
_Symbol type:_ `macro`
#### `FIO_LIST_PUSH`
```c
#define FIO_LIST_PUSH(head, n) \
do { \
(n)->prev = (head)->prev; \
(n)->next = (head); \
(head)->prev->next = (n); \
(head)->prev = (n); \
} while (0)
```
UNSAFE macro for pushing a node to a list.
_Symbol type:_ `macro`
#### `FIO_LIST_REMOVE`
```c
#define FIO_LIST_REMOVE(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
} while (0)
```
UNSAFE macro for removing a node from a list.
_Symbol type:_ `macro`
#### `FIO_LIST_REMOVE_RESET`
```c
#define FIO_LIST_REMOVE_RESET(n) \
do { \
(n)->prev->next = (n)->next; \
(n)->next->prev = (n)->prev; \
(n)->next = (n)->prev = (n); \
} while (0)
```
UNSAFE macro for removing a node from a list. Resets node data.
_Symbol type:_ `macro`
#### `FIO_LIST_POP`
```c
#define FIO_LIST_POP(type, node_name, dest_ptr, head) \
do { \
(dest_ptr) = FIO_PTR_FROM_FIELD(type, node_name, ((head)->next)); \
FIO_LIST_REMOVE(&(dest_ptr)->node_name); \
} while (0)
```
UNSAFE macro for popping a node to a list.
_Symbol type:_ `macro`
#### `FIO_LIST_IS_EMPTY`
```c
#define FIO_LIST_IS_EMPTY(head) \
((!(head)) || ((!(head)->next) | ((head)->next == (head))))
```
UNSAFE macro for testing if a list is empty.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST32_NODE`
```c
#define FIO_INDEXED_LIST32_NODE fio_index32_node_s
```
A 32 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST32_HEAD`
```c
#define FIO_INDEXED_LIST32_HEAD uint32_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST16_NODE`
```c
#define FIO_INDEXED_LIST16_NODE fio_index16_node_s
```
A 16 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST16_HEAD`
```c
#define FIO_INDEXED_LIST16_HEAD uint16_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST8_NODE`
```c
#define FIO_INDEXED_LIST8_NODE fio_index8_node_s
```
An 8 bit indexed linked list node type
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST8_HEAD`
```c
#define FIO_INDEXED_LIST8_HEAD uint8_t
```
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_PUSH`
```c
#define FIO_INDEXED_LIST_PUSH(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.prev = (root)[(head)].node_name.prev; \
(root)[n__].node_name.next = (head); \
(root)[(root)[(head)].node_name.prev].node_name.next = (n__); \
(root)[(head)].node_name.prev = (n__); \
} while (0)
```
UNSAFE macro for pushing a node to a list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_UNSHIFT`
```c
#define FIO_INDEXED_LIST_UNSHIFT(root, node_name, head, i) \
do { \
register const size_t n__ = (i); \
(root)[n__].node_name.next = (root)[(head)].node_name.next; \
(root)[n__].node_name.prev = (head); \
(root)[(root)[(head)].node_name.next].node_name.prev = (n__); \
(root)[(head)].node_name.next = (n__); \
(head) = (n__); \
} while (0)
```
UNSAFE macro for adding a node to the begging of the list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_REMOVE`
```c
#define FIO_INDEXED_LIST_REMOVE(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
} while (0)
```
UNSAFE macro for removing a node from a list.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_REMOVE_RESET`
```c
#define FIO_INDEXED_LIST_REMOVE_RESET(root, node_name, i) \
do { \
register const size_t n__ = (i); \
(root)[(root)[n__].node_name.prev].node_name.next = \
(root)[n__].node_name.next; \
(root)[(root)[n__].node_name.next].node_name.prev = \
(root)[n__].node_name.prev; \
(root)[n__].node_name.next = (root)[n__].node_name.prev = (n__); \
} while (0)
```
UNSAFE macro for removing a node from a list. Resets node data.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_EACH`
```c
#define FIO_INDEXED_LIST_EACH(root, node_name, head, pos) \
for (size_t pos = (head), \
stooper___hd = (head), \
stopper___ils___ = 0, \
pos##___nxt = (root)[(head)].node_name.next; \
!stopper___ils___; \
(stopper___ils___ = ((pos = pos##___nxt) == stooper___hd)), \
pos##___nxt = (root)[pos].node_name.next)
```
Loops through every index in the indexed list, assuming `head` is valid.
_Symbol type:_ `macro`
#### `FIO_INDEXED_LIST_EACH_REVERSED`
```c
#define FIO_INDEXED_LIST_EACH_REVERSED(root, node_name, head, pos) \
for (size_t pos = ((root)[(head)].node_name.prev), \
pos##___nxt = \
((root)[((root)[(head)].node_name.prev)].node_name.prev), \
stooper___hd = (head), \
stopper___ils___ = 0; \
!stopper___ils___; \
((stopper___ils___ = (pos == stooper___hd)), \
(pos = pos##___nxt), \
(pos##___nxt = (root)[pos##___nxt].node_name.prev)))
```
Loops through every index in the indexed list, assuming `head` is valid.
_Symbol type:_ `macro`
#### `FIO_LROT`
```c
#define FIO_LROT(i, bits) \
(((i) << ((bits) & ((sizeof((i)) << 3) - 1))) | \
((i) >> (((uint8_t)(-(bits))) & ((sizeof((i)) << 3) - 1))))
```
Left rotation for an unknown size element, inlined.
_Symbol type:_ `macro`
#### `FIO_RROT`
```c
#define FIO_RROT(i, bits) \
(((i) >> ((bits) & ((sizeof((i)) << 3) - 1))) | \
((i) << (((uint8_t)(-(bits))) & ((sizeof((i)) << 3) - 1))))
```
Right rotation for an unknown size element, inlined.
_Symbol type:_ `macro`
#### `FIO_HAS_ZERO_BYTE64`
```c
#define FIO_HAS_ZERO_BYTE64(row) \
(((row)-UINT64_C(0x0101010101010101)) & \
((~(row)) & UINT64_C(0x8080808080808080)))
```
_Symbol type:_ `macro`
#### `FIO_HAS_FULL_BYTE64`
```c
#define FIO_HAS_FULL_BYTE64(row) \
((((row)&UINT64_C(0x7F7F7F7F7F7F7F7F)) + UINT64_C(0x0101010101010101)) & \
(row)&UINT64_C(0x8080808080808080))
```
_Symbol type:_ `macro`
#### `FIO_HAS_BYTE2BITMAP`
```c
#define FIO_HAS_BYTE2BITMAP(result, bit_index) \
do { \
(result) = fio_ltole64((result)); /* map little endian to bitmap */ \
(result) >>= bit_index; /* move bit index to 0x01 */ \
(result) |= (result) >> 7; /* pack all 0x80 bits into one byte */ \
(result) |= (result) >> 14; \
(result) |= (result) >> 28; \
(result) &= 0xFFU; \
} while (0)
```
Converts a FIO_HAS_FULL_BYTE64 result to relative position bitmap.
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME0`
```c
#define FIO_U8_HASH_PRIME0 0x17U /* (4/6) 00010111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME1`
```c
#define FIO_U8_HASH_PRIME1 0x1DU /* (4/6) 00011101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME2`
```c
#define FIO_U8_HASH_PRIME2 0x25U /* (3/6) 00100101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME3`
```c
#define FIO_U8_HASH_PRIME3 0x29U /* (3/6) 00101001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME4`
```c
#define FIO_U8_HASH_PRIME4 0x2BU /* (4/6) 00101011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME5`
```c
#define FIO_U8_HASH_PRIME5 0x35U /* (4/6) 00110101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME6`
```c
#define FIO_U8_HASH_PRIME6 0x3BU /* (5/6) 00111011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME7`
```c
#define FIO_U8_HASH_PRIME7 0x43U /* (3/8) 01000011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME8`
```c
#define FIO_U8_HASH_PRIME8 0x47U /* (4/8) 01000111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME9`
```c
#define FIO_U8_HASH_PRIME9 0x49U /* (3/8) 01001001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME10`
```c
#define FIO_U8_HASH_PRIME10 0x53U /* (4/8) 01010011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME11`
```c
#define FIO_U8_HASH_PRIME11 0x59U /* (4/8) 01011001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME12`
```c
#define FIO_U8_HASH_PRIME12 0x61U /* (3/8) 01100001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME13`
```c
#define FIO_U8_HASH_PRIME13 0x65U /* (4/8) 01100101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME14`
```c
#define FIO_U8_HASH_PRIME14 0x67U /* (5/8) 01100111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME15`
```c
#define FIO_U8_HASH_PRIME15 0x6BU /* (5/8) 01101011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME16`
```c
#define FIO_U8_HASH_PRIME16 0x6DU /* (5/8) 01101101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME17`
```c
#define FIO_U8_HASH_PRIME17 0x83U /* (3/8) 10000011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME18`
```c
#define FIO_U8_HASH_PRIME18 0x89U /* (3/8) 10001001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME19`
```c
#define FIO_U8_HASH_PRIME19 0x8BU /* (4/8) 10001011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME20`
```c
#define FIO_U8_HASH_PRIME20 0x95U /* (4/8) 10010101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME21`
```c
#define FIO_U8_HASH_PRIME21 0x97U /* (5/8) 10010111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME22`
```c
#define FIO_U8_HASH_PRIME22 0x9DU /* (5/8) 10011101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME23`
```c
#define FIO_U8_HASH_PRIME23 0xA3U /* (4/8) 10100011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME24`
```c
#define FIO_U8_HASH_PRIME24 0xA7U /* (5/8) 10100111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME25`
```c
#define FIO_U8_HASH_PRIME25 0xADU /* (5/8) 10101101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME26`
```c
#define FIO_U8_HASH_PRIME26 0xB3U /* (5/8) 10110011 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME27`
```c
#define FIO_U8_HASH_PRIME27 0xB5U /* (5/8) 10110101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME28`
```c
#define FIO_U8_HASH_PRIME28 0xC1U /* (3/8) 11000001 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME29`
```c
#define FIO_U8_HASH_PRIME29 0xC5U /* (4/8) 11000101 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME30`
```c
#define FIO_U8_HASH_PRIME30 0xC7U /* (5/8) 11000111 */
```
_Symbol type:_ `macro`
#### `FIO_U8_HASH_PRIME31`
```c
#define FIO_U8_HASH_PRIME31 0xD3U /* (5/8) 11010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME0`
```c
#define FIO_U16_HASH_PRIME0 0x631DU /* 0110001100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME1`
```c
#define FIO_U16_HASH_PRIME1 0x4F19U /* 0100111100011001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME2`
```c
#define FIO_U16_HASH_PRIME2 0xA91BU /* 1010100100011011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME3`
```c
#define FIO_U16_HASH_PRIME3 0xDF01U /* 1101111100000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME4`
```c
#define FIO_U16_HASH_PRIME4 0x8C5DU /* 1000110001011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME5`
```c
#define FIO_U16_HASH_PRIME5 0xF941U /* 1111100101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME6`
```c
#define FIO_U16_HASH_PRIME6 0xC49DU /* 1100010010011101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME7`
```c
#define FIO_U16_HASH_PRIME7 0xA32BU /* 1010001100101011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME8`
```c
#define FIO_U16_HASH_PRIME8 0x7859U /* 0111100001011001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME9`
```c
#define FIO_U16_HASH_PRIME9 0xC4F1U /* 1100010011110001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME10`
```c
#define FIO_U16_HASH_PRIME10 0x74E1U /* 0111010011100001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME11`
```c
#define FIO_U16_HASH_PRIME11 0xD433U /* 1101010000110011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME12`
```c
#define FIO_U16_HASH_PRIME12 0xCB29U /* 1100101100101001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME13`
```c
#define FIO_U16_HASH_PRIME13 0xC2A7U /* 1100001010100111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME14`
```c
#define FIO_U16_HASH_PRIME14 0xC317U /* 1100001100010111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME15`
```c
#define FIO_U16_HASH_PRIME15 0x92B9U /* 1001001010111001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME16`
```c
#define FIO_U16_HASH_PRIME16 0x7D03U /* 0111110100000011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME17`
```c
#define FIO_U16_HASH_PRIME17 0x5CD1U /* 0101110011010001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME18`
```c
#define FIO_U16_HASH_PRIME18 0x73C1U /* 0111001111000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME19`
```c
#define FIO_U16_HASH_PRIME19 0x69A3U /* 0110100110100011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME20`
```c
#define FIO_U16_HASH_PRIME20 0xA2B3U /* 1010001010110011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME21`
```c
#define FIO_U16_HASH_PRIME21 0x521FU /* 0101001000011111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME22`
```c
#define FIO_U16_HASH_PRIME22 0x4E53U /* 0100111001010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME23`
```c
#define FIO_U16_HASH_PRIME23 0xFC41U /* 1111110001000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME24`
```c
#define FIO_U16_HASH_PRIME24 0x5F09U /* 0101111100001001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME25`
```c
#define FIO_U16_HASH_PRIME25 0x605FU /* 0110000001011111 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME26`
```c
#define FIO_U16_HASH_PRIME26 0xA715U /* 1010011100010101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME27`
```c
#define FIO_U16_HASH_PRIME27 0x6C65U /* 0110110001100101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME28`
```c
#define FIO_U16_HASH_PRIME28 0x65C5U /* 0110010111000101 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME29`
```c
#define FIO_U16_HASH_PRIME29 0x85D3U /* 1000010111010011 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME30`
```c
#define FIO_U16_HASH_PRIME30 0xDE41U /* 1101111001000001 */
```
_Symbol type:_ `macro`
#### `FIO_U16_HASH_PRIME31`
```c
#define FIO_U16_HASH_PRIME31 0xCA8DU /* 1100101010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME0`
```c
#define FIO_U32_HASH_PRIME0 0x618E9735 /* 01100001100011101001011100110101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME1`
```c
#define FIO_U32_HASH_PRIME1 0xD9E8E033 /* 11011001111010001110000000110011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME2`
```c
#define FIO_U32_HASH_PRIME2 0x50F116F9 /* 01010000111100010001011011111001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME3`
```c
#define FIO_U32_HASH_PRIME3 0x6E098F4B /* 01101110000010011000111101001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME4`
```c
#define FIO_U32_HASH_PRIME4 0x8CC87A6B /* 10001100110010000111101001101011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME5`
```c
#define FIO_U32_HASH_PRIME5 0x59E16F03 /* 01011001111000010110111100000011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME6`
```c
#define FIO_U32_HASH_PRIME6 0xBB838C63 /* 10111011100000111000110001100011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME7`
```c
#define FIO_U32_HASH_PRIME7 0x8532FF05 /* 10000101001100101111111100000101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME8`
```c
#define FIO_U32_HASH_PRIME8 0x44FEC4A5 /* 01000100111111101100010010100101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME9`
```c
#define FIO_U32_HASH_PRIME9 0x9B3350D5 /* 10011011001100110101000011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME10`
```c
#define FIO_U32_HASH_PRIME10 0x64BE6287 /* 01100100101111100110001010000111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME11`
```c
#define FIO_U32_HASH_PRIME11 0x57C2370B /* 01010111110000100011011100001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME12`
```c
#define FIO_U32_HASH_PRIME12 0x9E724F41 /* 10011110011100100100111101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME13`
```c
#define FIO_U32_HASH_PRIME13 0xF4A8A173 /* 11110100101010001010000101110011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME14`
```c
#define FIO_U32_HASH_PRIME14 0x6C4560FD /* 01101100010001010110000011111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME15`
```c
#define FIO_U32_HASH_PRIME15 0xD8558C3D /* 11011000010101011000110000111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME16`
```c
#define FIO_U32_HASH_PRIME16 0xC2F29157 /* 11000010111100101001000101010111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME17`
```c
#define FIO_U32_HASH_PRIME17 0xF4D03789 /* 11110100110100000011011110001001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME18`
```c
#define FIO_U32_HASH_PRIME18 0x9FB01857 /* 10011111101100000001100001010111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME19`
```c
#define FIO_U32_HASH_PRIME19 0xE9513C0F /* 11101001010100010011110000001111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME20`
```c
#define FIO_U32_HASH_PRIME20 0x89862FD3 /* 10001001100001100010111111010011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME21`
```c
#define FIO_U32_HASH_PRIME21 0xB742A51D /* 10110111010000101010010100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME22`
```c
#define FIO_U32_HASH_PRIME22 0xB3A681B9 /* 10110011101001101000000110111001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME23`
```c
#define FIO_U32_HASH_PRIME23 0xC44899F7 /* 11000100010010001001100111110111 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME24`
```c
#define FIO_U32_HASH_PRIME24 0x67DE8341 /* 01100111110111101000001101000001 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME25`
```c
#define FIO_U32_HASH_PRIME25 0xF453213B /* 11110100010100110010000100111011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME26`
```c
#define FIO_U32_HASH_PRIME26 0xD22F9855 /* 11010010001011111001100001010101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME27`
```c
#define FIO_U32_HASH_PRIME27 0x8B3E807D /* 10001011001111101000000001111101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME28`
```c
#define FIO_U32_HASH_PRIME28 0x59DD1C23 /* 01011001110111010001110000100011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME29`
```c
#define FIO_U32_HASH_PRIME29 0xEE548C8B /* 11101110010101001000110010001011 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME30`
```c
#define FIO_U32_HASH_PRIME30 0xD2E74E05 /* 11010010111001110100111000000101 */
```
_Symbol type:_ `macro`
#### `FIO_U32_HASH_PRIME31`
```c
#define FIO_U32_HASH_PRIME31 0x4E55788D /* 01001110010101010111100010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME0`
```c
#define FIO_U64_HASH_PRIME0 ((uint64_t)0x39664DEECA23D825) /* 0011100101100110010011011110111011001010001000111101100000100101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME1`
```c
#define FIO_U64_HASH_PRIME1 ((uint64_t)0x48644F7B3959621F) /* 0100100001100100010011110111101100111001010110010110001000011111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME2`
```c
#define FIO_U64_HASH_PRIME2 ((uint64_t)0x613A19F5CB0D98D5) /* 0110000100111010000110011111010111001011000011011001100011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME3`
```c
#define FIO_U64_HASH_PRIME3 ((uint64_t)0x84B56B93C869EA0F) /* 1000010010110101011010111001001111001000011010011110101000001111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME4`
```c
#define FIO_U64_HASH_PRIME4 ((uint64_t)0x8EE38D13E0D95A8D) /* 1000111011100011100011010001001111100000110110010101101010001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME5`
```c
#define FIO_U64_HASH_PRIME5 ((uint64_t)0x92E99EC981F0E279) /* 1001001011101001100111101100100110000001111100001110001001111001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME6`
```c
#define FIO_U64_HASH_PRIME6 ((uint64_t)0xDDC3100BEF158BB1) /* 1101110111000011000100000000101111101111000101011000101110110001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME7`
```c
#define FIO_U64_HASH_PRIME7 ((uint64_t)0x918F4D38049F78BD) /* 1001000110001111010011010011100000000100100111110111100010111101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME8`
```c
#define FIO_U64_HASH_PRIME8 ((uint64_t)0xB6C9F8032A35E2D9) /* 1011011011001001111110000000001100101010001101011110001011011001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME9`
```c
#define FIO_U64_HASH_PRIME9 ((uint64_t)0xFA2A5F16D2A128D5) /* 1111101000101010010111110001011011010010101000010010100011010101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME10`
```c
#define FIO_U64_HASH_PRIME10 ((uint64_t)0x5823C958ED5547D9) /* 0101100000100011110010010101100011101101010101010100011111011001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME11`
```c
#define FIO_U64_HASH_PRIME11 ((uint64_t)0xE8AB702EEE09CB43) /* 1110100010101011011100000010111011101110000010011100101101000011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME12`
```c
#define FIO_U64_HASH_PRIME12 ((uint64_t)0xEBD609356421F13D) /* 1110101111010110000010010011010101100100001000011111000100111101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME13`
```c
#define FIO_U64_HASH_PRIME13 ((uint64_t)0x43D0C330AF5B1F17) /* 0100001111010000110000110011000010101111010110110001111100010111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME14`
```c
#define FIO_U64_HASH_PRIME14 ((uint64_t)0xFEAE66D234871807) /* 1111111010101110011001101101001000110100100001110001100000000111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME15`
```c
#define FIO_U64_HASH_PRIME15 ((uint64_t)0xEE54B43A52941D6B) /* 1110111001010100101101000011101001010010100101000001110101101011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME16`
```c
#define FIO_U64_HASH_PRIME16 ((uint64_t)0x874E9DE46F15E205) /* 1000011101001110100111011110010001101111000101011110001000000101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME17`
```c
#define FIO_U64_HASH_PRIME17 ((uint64_t)0xFC7CA51A8A2E9171) /* 1111110001111100101001010001101010001010001011101001000101110001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME18`
```c
#define FIO_U64_HASH_PRIME18 ((uint64_t)0x83A70617F71F3C21) /* 1000001110100111000001100001011111110111000111110011110000100001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME19`
```c
#define FIO_U64_HASH_PRIME19 ((uint64_t)0x50FA705D6D99C11D) /* 0101000011111010011100000101110101101101100110011100000100011101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME20`
```c
#define FIO_U64_HASH_PRIME20 ((uint64_t)0x5362B5E6CF64814B) /* 0101001101100010101101011110011011001111011001001000000101001011 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME21`
```c
#define FIO_U64_HASH_PRIME21 ((uint64_t)0xA7A178389B0F3077) /* 1010011110100001011110000011100010011011000011110011000001110111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME22`
```c
#define FIO_U64_HASH_PRIME22 ((uint64_t)0x779D78921199BA45) /* 0111011110011101011110001001001000010001100110011011101001000101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME23`
```c
#define FIO_U64_HASH_PRIME23 ((uint64_t)0xB42FD16A9AE90F81) /* 1011010000101111110100010110101010011010111010010000111110000001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME24`
```c
#define FIO_U64_HASH_PRIME24 ((uint64_t)0xA2B4538A3C95576D) /* 1010001010110100010100111000101000111100100101010101011101101101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME25`
```c
#define FIO_U64_HASH_PRIME25 ((uint64_t)0x5E23D8E445F94E0D) /* 0101111000100011110110001110010001000101111110010100111000001101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME26`
```c
#define FIO_U64_HASH_PRIME26 ((uint64_t)0xE7CA493CD6444F07) /* 1110011111001010010010010011110011010110010001000100111100000111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME27`
```c
#define FIO_U64_HASH_PRIME27 ((uint64_t)0x734719A6A1873CB5) /* 0111001101000111000110011010011010100001100001110011110010110101 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME28`
```c
#define FIO_U64_HASH_PRIME28 ((uint64_t)0x56CCB954143A3AB7) /* 0101011011001100101110010101010000010100001110100011101010110111 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME29`
```c
#define FIO_U64_HASH_PRIME29 ((uint64_t)0xFA5BC5A72480BF81) /* 1111101001011011110001011010011100100100100000001011111110000001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME30`
```c
#define FIO_U64_HASH_PRIME30 ((uint64_t)0xD9B97D090C09F789) /* 1101100110111001011111010000100100001100000010011111011110001001 */
```
_Symbol type:_ `macro`
#### `FIO_U64_HASH_PRIME31`
```c
#define FIO_U64_HASH_PRIME31 ((uint64_t)0x9F74D0B9972E404B) /* 1001111101110100110100001011100110010111001011100100000001001011 */
```
_Symbol type:_ `macro`
#### `FIO_PRIME_TABLE_LIMIT`
```c
#define FIO_PRIME_TABLE_LIMIT 1024
```
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP`
```c
#define FIO_MATH_UXXX_OP(t, a, b, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = (a)[i__] op(b)[i__]; \
} while (0)
```
Performs `a op b` (+,-, *, etc') using easily vectorized loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_COP`
```c
#define FIO_MATH_UXXX_COP(t, a, b, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = (a)[i__] op(b); \
} while (0)
```
Performs `a op b` (+,-, *, etc'), where `b` is a constant.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_SOP`
```c
#define FIO_MATH_UXXX_SOP(t, a, bits, op) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = op(a)[i__]; \
} while (0)
```
Performs `t = op (a)`.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_RROT`
```c
#define FIO_MATH_UXXX_OP_RROT(t, a, b, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = \
((a)[i__] >> ((b)[i__] & ((bits)-1))) | \
((a)[i__] << (((bits) - ((b)[i__] & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a >> b) | (a << (bits - b))` (right rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_CRROT`
```c
#define FIO_MATH_UXXX_OP_CRROT(t, a, c, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = ((a)[i__] >> ((c) & ((bits)-1))) | \
((a)[i__] << (((bits) - ((c) & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a >> c) | (a << (bits - c))` (const right rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_LROT`
```c
#define FIO_MATH_UXXX_OP_LROT(t, a, b, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = \
((a)[i__] << ((b)[i__] & ((bits)-1))) | \
((a)[i__] >> (((bits) - ((b)[i__] & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a << b) | (a >> (bits - b))` (left rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_OP_CLROT`
```c
#define FIO_MATH_UXXX_OP_CLROT(t, a, c, bits) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = ((a)[i__] << ((c) & ((bits)-1))) | \
((a)[i__] >> (((bits) - ((c) & ((bits)-1))) & ((bits)-1))); \
} while (0)
```
Performs `(a << c) | (a >> (bits - c))` (const left rotation) in a loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_TOP`
```c
#define FIO_MATH_UXXX_TOP(t, a, b, c, bits, expr) \
do { \
for (size_t i__ = 0; i__ < (sizeof((t)) / sizeof((t)[0])); ++i__) \
(t)[i__] = expr((a)[i__], (b)[i__], (c)[i__]); \
} while (0)
```
Performs ternary `t = f(a, b, c)` lane-wise using easily vectorized loop.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_REDUCE`
```c
#define FIO_MATH_UXXX_REDUCE(t, a, bits, op) \
do { \
t = (a)[0]; \
for (size_t i__ = 1; i__ < (sizeof((a)) / sizeof((a)[0])); ++i__) \
(t) = (t)op(a)[i__]; \
} while (0)
```
Performs vector reduction for using `op` (+,-, *, etc'), storing to `t`.
_Symbol type:_ `macro`
#### `FIO_MATH_UXXX_SUFFLE`
```c
#define FIO_MATH_UXXX_SUFFLE(var, bits, ...) \
do { \
uint##bits##_t t____[sizeof((var)) / sizeof((var)[0])]; \
const uint8_t shuf____[sizeof((var)) / sizeof((var)[0])] = {__VA_ARGS__}; \
for (size_t i___ = 0; i___ < (sizeof((var)) / sizeof((var)[0])); ++i___) \
t____[i___] = (var)[shuf____[i___]]; \
for (size_t i___ = 0; i___ < (sizeof((var)) / sizeof((var)[0])); ++i___) \
(var)[i___] = t____[i___]; \
} while (0)
```
Performs vector shuffling (reordering) of `var`.
_Symbol type:_ `macro`
#### `FIO_VEC_SIMPLE_OP`
```c
#define FIO_VEC_SIMPLE_OP(result, a, b, len, op) \
FIO_FOR_UNROLL(len, sizeof(*a), i__, result[i__] = a[i__] op b[i__])
```
A math operation `op` between correlating vector positions of same length
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_OP`
```c
#define FIO_VEC_SCALAR_OP(result, v, sclr, len, op) \
FIO_FOR_UNROLL(len, sizeof(*(v)), i__, (result)[i__] = (v)[i__] op(sclr))
```
A math operation `op` between a scalar and a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_OP`
```c
#define FIO_VEC_REDUCE_OP(result, vec, len, op) \
FIO_FOR_UNROLL(len, sizeof(*vec), i__, result = result op vec[i__])
```
A math operation `op` between all vector members
_Symbol type:_ `macro`
#### `FIO_VEC_DOT`
```c
#define FIO_VEC_DOT(result, a, b, len) \
FIO_FOR_UNROLL(len, sizeof(*a), i__, result += a[i__] * b[i__])
```
The dot product of two vectors (sum(a[i]*b[i])).
_Symbol type:_ `macro`
#### `FIO_VEC_ADD`
```c
#define FIO_VEC_ADD(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, +)
```
Add two vectors of same length (adding corresponding positions)
_Symbol type:_ `macro`
#### `FIO_VEC_SUB`
```c
#define FIO_VEC_SUB(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, -)
```
Subtracting two vectors of same length
_Symbol type:_ `macro`
#### `FIO_VEC_MUL`
```c
#define FIO_VEC_MUL(result, a, b, len) FIO_VEC_SIMPLE_OP(result, a, b, len, *)
```
Multiplying two vectors (multiplying corresponding positions - half dot)
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_ADD`
```c
#define FIO_VEC_SCALAR_ADD(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, +)
```
Add scalar to vector
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_SUB`
```c
#define FIO_VEC_SCALAR_SUB(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, -)
```
Subtract scalar from vector
_Symbol type:_ `macro`
#### `FIO_VEC_SCALAR_MUL`
```c
#define FIO_VEC_SCALAR_MUL(result, vec, scalar, vlen) \
FIO_VEC_SCALAR_OP(result, vec, scalar, vlen, *)
```
Multiply scalar from vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_ADD`
```c
#define FIO_VEC_REDUCE_ADD(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, +)
```
Add all members of a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_SUB`
```c
#define FIO_VEC_REDUCE_SUB(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, -)
```
Subtract all members of a vector
_Symbol type:_ `macro`
#### `FIO_VEC_REDUCE_MUL`
```c
#define FIO_VEC_REDUCE_MUL(result, vec, vlen) \
FIO_VEC_REDUCE_OP(result, vec, vlen, *)
```
Multiply all members of a vector
_Symbol type:_ `macro`
#### `FIO_DEFINE_RANDOM128_FN`
```c
#define FIO_DEFINE_RANDOM128_FN(extern, name, reseed_log, seed_offset) \
static uint64_t name##___state[9] FIO_ALIGN(64) = { \
0x9c65875be1fce7b9ULL + seed_offset, \
0x7cc568e838f6a40dULL, \
0x4bb8d885a0fe47d5ULL + seed_offset, \
0x95561f0927ad7ecdULL, \
0}; \
extern FIO_MAYBE_UNUSED void name##_reset(void) { \
name##___state[0] = 0x9c65875be1fce7b9ULL + seed_offset; \
name##___state[1] = 0x7cc568e838f6a40dULL; \
name##___state[2] = 0x4bb8d885a0fe47d5ULL + seed_offset; \
name##___state[3] = 0x95561f0927ad7ecdULL; \
name##___state[8] = name##___state[4] = 0; \
} \
extern void name##_reseed(void) { \
const size_t jitter_samples = 16 | (name##___state[0] & 15); \
for (size_t i = 0; i < jitter_samples; ++i) { \
struct timespec t; \
clock_gettime(CLOCK_MONOTONIC, &t); \
uint64_t clk[2] = {(uint64_t)(uintptr_t)&clk + (uint64_t)(uintptr_t) & \
(name##_reseed), \
0}; \
clk[0] += (uint64_t)((t.tv_sec << 30) + (int64_t)t.tv_nsec); \
clk[0] = fio_math_mulc64(clk[0], FIO_U64_HASH_PRIME0, clk + 1); \
clk[1] += FIO_U64_HASH_PRIME0; \
clk[0] += fio_lrot64(clk[0], 27); \
clk[0] += fio_lrot64(clk[0], 49); \
clk[1] += fio_lrot64(clk[1], 27); \
clk[1] += fio_lrot64(clk[1], 49); \
name##___state[0] += clk[0] + fio_cycle_counter(); \
name##___state[1] += clk[1] + fio_cycle_counter(); \
name##___state[2] += clk[0] + fio_cycle_counter(); \
name##___state[3] += clk[1] + fio_cycle_counter(); \
} \
name##___state[8] += ((name##___state[8] & 7) + (name##___state[0] & 30)); \
} \
/** Re-seeds the PNGR so forked processes don't match. */ \
extern FIO_MAYBE_UNUSED void name##_on_fork(void *is_null) { \
(void)is_null; \
name##_reseed(); \
} \
/** Returns a 128 bit pseudo-random number. */ \
extern FIO_MAYBE_UNUSED fio_u128 name##128(void) { \
fio_u128 r; \
uint64_t s0[4]; \
const uint64_t counter = fio_atomic_add(name##___state + 4, 1); \
if (!(counter & ((1ULL << reseed_log) - 1)) && \
((size_t)(reseed_log - 1) < 63)) \
name##_reseed(); \
{ /* load state + per-call input (timing, counter, address variation) */ \
const uint64_t cycles = \
reseed_log ? fio_cycle_counter() + (uint64_t)(uintptr_t)&cycles \
: 0xB5ULL; \
const uint64_t variation = \
0x4E55788DULL + \
(reseed_log ? (uint64_t)(uintptr_t)&name##_reseed : 0); \
s0[0] = name##___state[0] + cycles + counter; \
s0[1] = name##___state[1] + cycles + (seed_offset + 0x59DD1C23ULL); \
s0[2] = name##___state[2] + cycles + counter; \
s0[3] = name##___state[3] + cycles + variation; \
} \
{ /* update: one Feistel layer (lanes 0,2 from 1,3), then rotate lanes */ \
const uint64_t a_ = \
s0[0] + fio_math_mul64_fold(s0[1] ^ FIO_U64_HASH_PRIME0, \
s0[3] ^ FIO_U64_HASH_PRIME1); \
const uint64_t c_ = \
s0[2] + fio_math_mul64_fold(s0[3] ^ FIO_U64_HASH_PRIME2, \
s0[1] ^ FIO_U64_HASH_PRIME3); \
s0[0] = s0[1]; /* rotate: the other pair is updated next call */ \
s0[1] = c_; \
s0[2] = s0[3]; \
s0[3] = a_; \
} \
/* output: two multiply layers over all lanes (never stored) */ \
r.u64[0] = fio_math_mul64_fold(s0[0] ^ FIO_U64_HASH_PRIME8, \
s0[1] ^ FIO_U64_HASH_PRIME9) + \
fio_math_mul64_fold(s0[2] ^ FIO_U64_HASH_PRIME10, \
s0[3] ^ FIO_U64_HASH_PRIME11); \
r.u64[1] = fio_math_mul64_fold(s0[0] ^ FIO_U64_HASH_PRIME12, \
s0[3] ^ FIO_U64_HASH_PRIME13) + \
fio_math_mul64_fold(s0[1] ^ FIO_U64_HASH_PRIME14, \
s0[2] ^ FIO_U64_HASH_PRIME15); \
for (size_t i = 0; i < 4; ++i) /* store to memory */ \
name##___state[i] = s0[i]; \
return r; \
} \
/** Returns a 64 bit pseudo-random number. */ \
extern FIO_MAYBE_UNUSED uint64_t name##64(void) { \
static fio_u128 r; \
size_t counter = fio_atomic_add(name##___state + 8, 1); \
if (!(counter & 1)) \
r = name##128(); \
return r.u64[counter & 1]; \
} \
/** Fills the `dest` buffer with pseudo-random noise. */ \
extern FIO_MAYBE_UNUSED void name##_bytes(void *dest, size_t len) { \
if (!dest || !len) \
return; \
uint8_t *d = (uint8_t *)dest; \
for (unsigned i = 15; i < len; i += 16) { \
fio_u128 r = name##128(); \
fio_memcpy16(d, r.u8); \
d += 16; \
} \
if (len & 15) { \
fio_u128 r = name##128(); \
fio_memcpy15x(d, r.u8, len); \
} \
}
```
Defines a semi-deterministic Pseudo-Random 128 bit Number Generator function.
The following functions will be defined:
- extern fio_u128 name##128(void); // returns 128 bits
- extern uint64_t name##64(void); // returns 64 bits (simply half the result)
- extern void name##_bytes(void *buffer, size_t len); // fills a buffer
- extern void name##_reset(void); // resets the state of the PRNG
- extern void name##_on_fork(void * is_null); // reseeds the PRNG
If `reseed_log` is non-zero and less than 64, the PNGR is no longer
deterministic, as it will automatically re-seeds itself every 2^reseed_log
iterations.
If `extern` is `static` or `FIO_SFUNC`, static function will be defined.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_IS_EQ`
```c
#define FIO_STR_INFO_IS_EQ(s1, s2) \
((s1).len == (s2).len && \
(!(s1).len || (s1).buf == (s2).buf || \
((s1).buf && (s2).buf && (s1).buf[0] == (s2).buf[0] && \
!FIO_MEMCMP((s1).buf, (s2).buf, (s1).len))))
```
Compares two `fio_str_info_s` objects for content equality.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO_IS_EQ`
```c
#define FIO_BUF_INFO_IS_EQ(s1, s2) FIO_STR_INFO_IS_EQ((s1), (s2))
```
Compares two `fio_buf_info_s` objects for content equality.
_Symbol type:_ `macro`
#### `FIO_STR_INFO0`
```c
#define FIO_STR_INFO0 ((fio_str_info_s){0})
```
A NULL fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO1`
```c
#define FIO_STR_INFO1(str) \
((fio_str_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO2`
```c
#define FIO_STR_INFO2(str, length) \
((fio_str_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO3`
```c
#define FIO_STR_INFO3(str, length, capacity) \
((fio_str_info_s){.len = (length), .buf = (str), .capa = (capacity)})
```
Converts a String with a known length and capacity into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO0`
```c
#define FIO_BUF_INFO0 ((fio_buf_info_s){0})
```
A NULL fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO1`
```c
#define FIO_BUF_INFO1(str) \
((fio_buf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF_INFO2`
```c
#define FIO_BUF_INFO2(str, length) \
((fio_buf_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_buf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO0`
```c
#define FIO_UBUF_INFO0 ((fio_ubuf_info_s){0})
```
A NULL fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO1`
```c
#define FIO_UBUF_INFO1(str) \
((fio_ubuf_info_s){.len = ((str) ? FIO_STRLEN((str)) : 0), .buf = (str)})
```
Converts a C String into a fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_UBUF_INFO2`
```c
#define FIO_UBUF_INFO2(str, length) \
((fio_ubuf_info_s){.len = (length), .buf = (str)})
```
Converts a String with a known length into a fio_ubuf_info_s.
_Symbol type:_ `macro`
#### `FIO_BUF2STR_INFO`
```c
#define FIO_BUF2STR_INFO(buf_info) \
((fio_str_info_s){.len = (buf_info).len, .buf = (buf_info).buf})
```
Converts a fio_buf_info_s into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR2BUF_INFO`
```c
#define FIO_STR2BUF_INFO(str_info) \
((fio_buf_info_s){.len = (str_info).len, .buf = (str_info).buf})
```
Converts a fio_buf_info_s into a fio_str_info_s.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_TMP_VAR`
```c
#define FIO_STR_INFO_TMP_VAR(name, capacity) \
char fio___stack_mem___##name[(capacity) + 1]; \
fio___stack_mem___##name[0] = 0; /* guard */ \
fio_str_info_s name = (fio_str_info_s) { \
.buf = fio___stack_mem___##name, .capa = (capacity) \
}
```
Creates a stack fio_str_info_s variable `name` with `capacity` bytes.
_Symbol type:_ `macro`
#### `FIO_STR_INFO_TMP_IS_REALLOCATED`
```c
#define FIO_STR_INFO_TMP_IS_REALLOCATED(name) \
(fio___stack_mem___##name != name.buf)
```
Tests to see if memory reallocation happened.
_Symbol type:_ `macro`
#### `FIO_UTF8_ALLOW_IF`
```c
#define FIO_UTF8_ALLOW_IF 1
```
_Symbol type:_ `macro`
### Types
#### `fio_lock_i`
```c
typedef volatile unsigned char fio_lock_i
```
A spin lock type, must be initialized to `FIO_LOCK_INIT` before use.
_Symbol type:_ `type`
#### `fio_list_node_s`
```c
struct fio_list_node_s {
struct fio_list_node_s *next;
struct fio_list_node_s *prev;
}
```
A linked list arch-type
_Symbol type:_ `type`
#### `fio_index32_node_s`
```c
struct fio_index32_node_s {
uint32_t next;
uint32_t prev;
}
```
A 32 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_index16_node_s`
```c
struct fio_index16_node_s {
uint16_t next;
uint16_t prev;
}
```
A 16 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_index8_node_s`
```c
struct fio_index8_node_s {
uint8_t next;
uint8_t prev;
}
```
An 8 bit indexed linked list node type
_Symbol type:_ `type`
#### `fio_u128`
```c
union fio_u128 {
FIO___UXXX_UGRP_DEF(128);
#if defined(__SIZEOF_INT128__)
__uint128_t alignment_for_u128_[1];
#endif
}
```
An unsigned 128bit union type.
_Symbol type:_ `type`
#### `fio_u256`
```c
union fio_u256 {
fio_u128 u128[2];
FIO___UXXX_UGRP_DEF(256);
#if defined(__SIZEOF_INT256__)
__uint256_t alignment_for_u256_[1];
#endif
}
```
An unsigned 256bit union type.
_Symbol type:_ `type`
#### `fio_u512`
```c
union fio_u512 {
fio_u128 u128[4];
fio_u256 u256[2];
FIO___UXXX_UGRP_DEF(512);
}
```
An unsigned 512bit union type.
_Symbol type:_ `type`
#### `fio_u1024`
```c
union fio_u1024 {
fio_u128 u128[8];
fio_u256 u256[4];
fio_u512 u512[2];
FIO___UXXX_UGRP_DEF(1024);
}
```
An unsigned 1024bit union type.
_Symbol type:_ `type`
#### `fio_u2048`
```c
union fio_u2048 {
fio_u128 u128[16];
fio_u256 u256[8];
fio_u512 u512[4];
fio_u1024 u1024[2];
FIO___UXXX_UGRP_DEF(2048);
}
```
An unsigned 2048bit union type.
_Symbol type:_ `type`
#### `fio_u4096`
```c
union fio_u4096 {
fio_u128 u128[32];
fio_u256 u256[16];
fio_u512 u512[8];
fio_u1024 u1024[4];
fio_u2048 u2048[2];
FIO___UXXX_UGRP_DEF(4096);
}
```
An unsigned 4096bit union type.
_Symbol type:_ `type`
#### `fio_str_info_s`
```c
struct fio_str_info_s {
/** The string's length, if any. */
size_t len;
/** The string's buffer (pointer to first byte) or NULL on error. */
char *buf;
/** The buffer's capacity. Zero (0) indicates the buffer is read-only. */
size_t capa;
}
```
An information type for reporting the string's state.
_Symbol type:_ `type`
#### `fio_buf_info_s`
```c
struct fio_buf_info_s {
/** The buffer's length, if any. */
size_t len;
/** The buffer's address (may be NULL if no buffer). */
char *buf;
}
```
An information type for reporting/storing buffer data (no `capa`).
_Symbol type:_ `type`
#### `fio_ubuf_info_s`
```c
struct fio_ubuf_info_s {
/** The buffer's length, if any. */
size_t len;
/** The buffer's address (may be NULL if no buffer). */
unsigned char *buf;
}
```
An information type for reporting/storing unsigned buffer data.
_Symbol type:_ `type`
### Functions
#### `fio_getpid`
```c
#define fio_getpid _getpid
```
Calls the `getpid` provided by the system.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_load`
```c
#define fio_atomic_load(dest, p_obj) \
do { \
dest = __atomic_load_n((p_obj), __ATOMIC_SEQ_CST); \
} while (0)
```
An atomic load operation, returns value in pointer.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_compare_exchange_p`
```c
#define fio_atomic_compare_exchange_p(p_obj, p_expected, p_desired) __atomic_compare_exchange((p_obj), (p_expected), (p_desired), 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
```
An atomic compare and exchange operation, returns true if an exchange occurred. `p_expected` MAY be overwritten with the existing value (system specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_exchange`
```c
#define fio_atomic_exchange(p_obj, value) __atomic_exchange_n((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic exchange operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_add`
```c
#define fio_atomic_add(p_obj, value) __atomic_fetch_add((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic addition operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_sub`
```c
#define fio_atomic_sub(p_obj, value) __atomic_fetch_sub((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic subtraction operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_and`
```c
#define fio_atomic_and(p_obj, value) __atomic_fetch_and((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic AND (&) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_xor`
```c
#define fio_atomic_xor(p_obj, value) __atomic_fetch_xor((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic XOR (^) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_or`
```c
#define fio_atomic_or(p_obj, value) __atomic_fetch_or((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic OR (|) operation, returns previous value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_add_fetch`
```c
#define fio_atomic_add_fetch(p_obj, value) __atomic_add_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic addition operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_sub_fetch`
```c
#define fio_atomic_sub_fetch(p_obj, value) __atomic_sub_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic subtraction operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_and_fetch`
```c
#define fio_atomic_and_fetch(p_obj, value) __atomic_and_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic AND (&) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_xor_fetch`
```c
#define fio_atomic_xor_fetch(p_obj, value) __atomic_xor_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic XOR (^) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_atomic_or_fetch`
```c
#define fio_atomic_or_fetch(p_obj, value) __atomic_or_fetch((p_obj), (value), __ATOMIC_SEQ_CST)
```
An atomic OR (|) operation, returns new value
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_trylock_group`
```c
inline uint8_t fio_trylock_group(fio_lock_i *lock, uint8_t group)
```
Tries to lock a group of sublocks.
Combine a number of sublocks using OR (`|`) and the FIO_LOCK_SUBLOCK(i)
macro. i.e.:
```c
if(!fio_trylock_group(&lock,
FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2))) {
// act in lock
}
fio_unlock_group(&lock, FIO_LOCK_SUBLOCK(1) | FIO_LOCK_SUBLOCK(2)));
```
Returns 0 on success and non-zero on failure.
_Symbol type:_ `function`
#### `fio_lock_group`
```c
inline void fio_lock_group(fio_lock_i *lock, uint8_t group)
```
Busy waits for a group lock to become available - not recommended.
See `fio_trylock_group` for details.
_Symbol type:_ `function`
#### `fio_unlock_group`
```c
inline void fio_unlock_group(fio_lock_i *lock, uint8_t group)
```
Unlocks a sublock group, no matter which thread owns which sublock.
_Symbol type:_ `function`
#### `fio_trylock_full`
```c
inline uint8_t fio_trylock_full(fio_lock_i *lock)
```
Tries to lock all sublocks. Returns 0 on success and 1 on failure.
_Symbol type:_ `function`
#### `fio_lock_full`
```c
inline void fio_lock_full(fio_lock_i *lock)
```
Busy waits for all sub lock to become available - not recommended.
_Symbol type:_ `function`
#### `fio_unlock_full`
```c
inline void fio_unlock_full(fio_lock_i *lock)
```
Unlocks all sub locks, no matter which thread owns the lock.
_Symbol type:_ `function`
#### `fio_trylock`
```c
inline uint8_t fio_trylock(fio_lock_i *lock)
```
Tries to acquire the default lock (sublock 0).
Returns 0 on success and 1 on failure.
_Symbol type:_ `function`
#### `fio_lock`
```c
inline void fio_lock(fio_lock_i *lock)
```
Busy waits for the default lock to become available - not recommended.
_Symbol type:_ `function`
#### `fio_unlock`
```c
inline void fio_unlock(fio_lock_i *lock)
```
Unlocks the default lock, no matter which thread owns the lock.
_Symbol type:_ `function`
#### `fio_is_locked`
```c
inline uint8_t fio_is_locked(fio_lock_i *lock)
```
Returns 1 if the lock is locked, 0 otherwise.
_Symbol type:_ `function`
#### `fio_is_group_locked`
```c
inline uint8_t fio_is_group_locked(fio_lock_i *lock, uint8_t group)
```
Returns 1 if the lock is locked, 0 otherwise.
_Symbol type:_ `function`
#### `fio_atomic_bit_get`
```c
inline uint8_t fio_atomic_bit_get(void *map, size_t bit)
```
Gets the state of a bit in a bitmap.
_Symbol type:_ `function`
#### `fio_atomic_bit_set`
```c
inline void fio_atomic_bit_set(void *map, size_t bit)
```
Sets the a bit in a bitmap (sets to 1).
_Symbol type:_ `function`
#### `fio_atomic_bit_unset`
```c
inline void fio_atomic_bit_unset(void *map, size_t bit)
```
Unsets the a bit in a bitmap (sets to 0).
_Symbol type:_ `function`
#### `fio_atomic_bit_flip`
```c
inline void fio_atomic_bit_flip(void *map, size_t bit)
```
Flips the a bit in a bitmap (sets to 0 if 1, sets to 1 if 0).
_Symbol type:_ `function`
#### `fio_is_little_endian`
```c
inline unsigned int fio_is_little_endian(void)
```
Dynamically test if we are on a little endian machine.
_Symbol type:_ `function`
#### `fio_is_big_endian`
```c
inline unsigned int fio_is_big_endian(void)
```
Dynamically test if we are on a big endian machine.
_Symbol type:_ `function`
#### `fio_memcpy0`
```c
FIO_SFUNC void *fio_memcpy0(void *restrict d, const void *restrict s)
```
No-op (completes the name space).
_Symbol type:_ `function`
#### `fio_memcpy1`
```c
void * fio_memcpy1(void *restrict d, const void *restrict s)
```
Copies 1 byte from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2`
```c
void * fio_memcpy2(void *restrict d, const void *restrict s)
```
Copies 2 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy3`
```c
void * fio_memcpy3(void *restrict d, const void *restrict s)
```
Copies 3 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4`
```c
void * fio_memcpy4(void *restrict d, const void *restrict s)
```
Copies 4 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy5`
```c
void * fio_memcpy5(void *restrict d, const void *restrict s)
```
Copies 5 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy6`
```c
void * fio_memcpy6(void *restrict d, const void *restrict s)
```
Copies 6 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy7`
```c
void * fio_memcpy7(void *restrict d, const void *restrict s)
```
Copies 7 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy8`
```c
void * fio_memcpy8(void *restrict d, const void *restrict s)
```
Copies 8 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy16`
```c
void * fio_memcpy16(void *restrict d, const void *restrict s)
```
Copies 16 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy32`
```c
void * fio_memcpy32(void *restrict d, const void *restrict s)
```
Copies 32 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy64`
```c
void * fio_memcpy64(void *restrict d, const void *restrict s)
```
Copies 64 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy128`
```c
void * fio_memcpy128(void *restrict d, const void *restrict s)
```
Copies 128 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy256`
```c
void * fio_memcpy256(void *restrict d, const void *restrict s)
```
Copies 256 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy512`
```c
void * fio_memcpy512(void *restrict d, const void *restrict s)
```
Copies 512 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy1024`
```c
void * fio_memcpy1024(void *restrict d, const void *restrict s)
```
Copies 1024 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2048`
```c
void * fio_memcpy2048(void *restrict d, const void *restrict s)
```
Copies 2048 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4096`
```c
void * fio_memcpy4096(void *restrict d, const void *restrict s)
```
Copies 4096 bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy0x`
```c
FIO_SFUNC void *fio_memcpy0x(void *d, const void *s, size_t l)
```
No-op (completes the name space).
_Symbol type:_ `function`
#### `fio_memcpy7x`
```c
void * fio_memcpy7x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy15x`
```c
void * fio_memcpy15x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy31x`
```c
void * fio_memcpy31x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy63x`
```c
void * fio_memcpy63x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy127x`
```c
void * fio_memcpy127x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy255x`
```c
void * fio_memcpy255x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy511x`
```c
void * fio_memcpy511x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy1023x`
```c
void * fio_memcpy1023x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy2047x`
```c
void * fio_memcpy2047x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_memcpy4095x`
```c
void * fio_memcpy4095x(void *restrict d, const void *restrict s, size_t len)
```
Copies up to `len & lim` bytes from `src` (`s`) to `dest` (`d`).
_Symbol type:_ `function`
#### `fio_bswap8`
```c
#define fio_bswap8(i) (i)
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap16`
```c
#define fio_bswap16(i) __builtin_bswap16((uint16_t)(i))
```
Byte swap a 16 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap32`
```c
#define fio_bswap32(i) __builtin_bswap32((uint32_t)(i))
```
Byte swap a 32 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap64`
```c
#define fio_bswap64(i) __builtin_bswap64((uint64_t)(i))
```
Byte swap a 64 bit integer, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap128`
```c
#define fio_bswap128(i) __builtin_bswap128((__uint128_t)(i))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bswap128`
```c
inline FIO_CONST __uint128_t fio_bswap128(__uint128_t i)
```
_Symbol type:_ `function`
#### `fio_ltole8`
```c
#define fio_ltole8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton8`
```c
#define fio_lton8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol8`
```c
#define fio_ntol8(i) (i) /* avoid special cases by defining for all sizes */
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton16`
```c
#define fio_lton16(i) (i)
```
Local byte order to Network byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton32`
```c
#define fio_lton32(i) (i)
```
Local byte order to Network byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton64`
```c
#define fio_lton64(i) (i)
```
Local byte order to Network byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole16`
```c
#define fio_ltole16(i) fio_bswap16((i))
```
Local byte order to Little Endian byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole32`
```c
#define fio_ltole32(i) fio_bswap32((i))
```
Local byte order to Little Endian byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole64`
```c
#define fio_ltole64(i) fio_bswap64((i))
```
Local byte order to Little Endian byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol16`
```c
#define fio_ntol16(i) (i)
```
Network byte order to Local byte order, 16 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol32`
```c
#define fio_ntol32(i) (i)
```
Network byte order to Local byte order, 32 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol64`
```c
#define fio_ntol64(i) (i)
```
Network byte order to Local byte order, 62 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ntol128`
```c
#define fio_ntol128(i) (i)
```
Network byte order to Local byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ltole128`
```c
#define fio_ltole128(i) fio_bswap128((i))
```
Local byte order to Little Endian byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lton128`
```c
#define fio_lton128(i) fio_bswap128((i))
```
Local byte order to Network byte order, 128 bit integer
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_buf2u8u`
```c
inline uint8_t fio_buf2u8u(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8u`
```c
inline void fio_u2buf8u(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u8_le`
```c
inline uint8_t fio_buf2u8_le(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8_le`
```c
inline void fio_u2buf8_le(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u8_be`
```c
inline uint8_t fio_buf2u8_be(const void *c)
```
Converts an unaligned byte stream to an 8 bit number.
_Symbol type:_ `function`
#### `fio_u2buf8_be`
```c
inline void fio_u2buf8_be(void *buf, uint8_t i)
```
Writes a local 8 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16u`
```c
uint16_t fio_buf2u16u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16u`
```c
void fio_u2buf16u(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32u`
```c
uint32_t fio_buf2u32u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32u`
```c
void fio_u2buf32u(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64u`
```c
uint64_t fio_buf2u64u(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64u`
```c
void fio_u2buf64u(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16_le`
```c
uint16_t fio_buf2u16_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16_le`
```c
void fio_u2buf16_le(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32_le`
```c
uint32_t fio_buf2u32_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32_le`
```c
void fio_u2buf32_le(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64_le`
```c
uint64_t fio_buf2u64_le(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64_le`
```c
void fio_u2buf64_le(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u16_be`
```c
uint16_t fio_buf2u16_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf16_be`
```c
void fio_u2buf16_be(void *buf, uint16_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u32_be`
```c
uint32_t fio_buf2u32_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf32_be`
```c
void fio_u2buf32_be(void *buf, uint32_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u64_be`
```c
uint64_t fio_buf2u64_be(const void *c)
```
Converts an unaligned byte stream to a bits bit number.
_Symbol type:_ `function`
#### `fio_u2buf64_be`
```c
void fio_u2buf64_be(void *buf, uint64_t i)
```
Writes a bits bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24_le`
```c
inline uint32_t fio_buf2u24_le(const void *c)
```
Converts an unaligned byte stream to a 24 bit number.
_Symbol type:_ `function`
#### `fio_u2buf24_le`
```c
inline void fio_u2buf24_le(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24_be`
```c
inline uint32_t fio_buf2u24_be(const void *c)
```
Converts an unaligned byte stream to a 24 bit number.
_Symbol type:_ `function`
#### `fio_u2buf24_be`
```c
inline void fio_u2buf24_be(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer.
_Symbol type:_ `function`
#### `fio_buf2u24u`
```c
inline uint32_t fio_buf2u24u(const void *c)
```
Converts an unaligned byte stream to a 24 bit number - local endieness.
_Symbol type:_ `function`
#### `fio_u2buf24u`
```c
inline void fio_u2buf24u(void *buf, uint32_t i)
```
Writes a 24 bit number to an unaligned buffer - in local endieness.
_Symbol type:_ `function`
#### `fio_buf2zu`
```c
inline size_t fio_buf2zu(const void *c)
```
Converts an unaligned byte stream to a size_t - local endieness.
_Symbol type:_ `function`
#### `fio_u2bufzu`
```c
inline void fio_u2bufzu(void *buf, size_t i)
```
Writes a size_t to an unaligned buffer - in local endieness.
_Symbol type:_ `function`
#### `fio_u8x4_add`
```c
void fio_u8x4_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_and`
```c
void fio_u8x4_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_mul`
```c
void fio_u8x4_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_or`
```c
void fio_u8x4_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_add`
```c
uint8_t fio_u8x4_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_and`
```c
uint8_t fio_u8x4_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_max`
```c
uint8_t fio_u8x4_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_min`
```c
uint8_t fio_u8x4_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_mul`
```c
uint8_t fio_u8x4_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_or`
```c
uint8_t fio_u8x4_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_sub`
```c
uint8_t fio_u8x4_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reduce_xor`
```c
uint8_t fio_u8x4_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x4_reshuffle`
```c
void fio_u8x4_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x4_sub`
```c
void fio_u8x4_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x4_xor`
```c
void fio_u8x4_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_add`
```c
void fio_u8x8_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_and`
```c
void fio_u8x8_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_mul`
```c
void fio_u8x8_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_or`
```c
void fio_u8x8_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_add`
```c
uint8_t fio_u8x8_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_and`
```c
uint8_t fio_u8x8_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_max`
```c
uint8_t fio_u8x8_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_min`
```c
uint8_t fio_u8x8_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_mul`
```c
uint8_t fio_u8x8_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_or`
```c
uint8_t fio_u8x8_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_sub`
```c
uint8_t fio_u8x8_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reduce_xor`
```c
uint8_t fio_u8x8_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x8_reshuffle`
```c
void fio_u8x8_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x8_sub`
```c
void fio_u8x8_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x8_xor`
```c
void fio_u8x8_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_add`
```c
void fio_u8x16_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_and`
```c
void fio_u8x16_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_mul`
```c
void fio_u8x16_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_or`
```c
void fio_u8x16_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_add`
```c
uint8_t fio_u8x16_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_and`
```c
uint8_t fio_u8x16_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_max`
```c
uint8_t fio_u8x16_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_min`
```c
uint8_t fio_u8x16_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_mul`
```c
uint8_t fio_u8x16_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_or`
```c
uint8_t fio_u8x16_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_sub`
```c
uint8_t fio_u8x16_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reduce_xor`
```c
uint8_t fio_u8x16_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x16_reshuffle`
```c
void fio_u8x16_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x16_sub`
```c
void fio_u8x16_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x16_xor`
```c
void fio_u8x16_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_add`
```c
void fio_u8x32_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_and`
```c
void fio_u8x32_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_mul`
```c
void fio_u8x32_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_or`
```c
void fio_u8x32_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_add`
```c
uint8_t fio_u8x32_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_and`
```c
uint8_t fio_u8x32_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_max`
```c
uint8_t fio_u8x32_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_min`
```c
uint8_t fio_u8x32_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_mul`
```c
uint8_t fio_u8x32_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_or`
```c
uint8_t fio_u8x32_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_sub`
```c
uint8_t fio_u8x32_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reduce_xor`
```c
uint8_t fio_u8x32_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x32_reshuffle`
```c
void fio_u8x32_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x32_sub`
```c
void fio_u8x32_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x32_xor`
```c
void fio_u8x32_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_add`
```c
void fio_u8x64_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_and`
```c
void fio_u8x64_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_mul`
```c
void fio_u8x64_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_or`
```c
void fio_u8x64_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_add`
```c
uint8_t fio_u8x64_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_and`
```c
uint8_t fio_u8x64_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_max`
```c
uint8_t fio_u8x64_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_min`
```c
uint8_t fio_u8x64_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_mul`
```c
uint8_t fio_u8x64_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_or`
```c
uint8_t fio_u8x64_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_sub`
```c
uint8_t fio_u8x64_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reduce_xor`
```c
uint8_t fio_u8x64_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x64_reshuffle`
```c
void fio_u8x64_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x64_sub`
```c
void fio_u8x64_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x64_xor`
```c
void fio_u8x64_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_add`
```c
void fio_u8x128_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_and`
```c
void fio_u8x128_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_mul`
```c
void fio_u8x128_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_or`
```c
void fio_u8x128_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_add`
```c
uint8_t fio_u8x128_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_and`
```c
uint8_t fio_u8x128_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_max`
```c
uint8_t fio_u8x128_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_min`
```c
uint8_t fio_u8x128_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_mul`
```c
uint8_t fio_u8x128_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_or`
```c
uint8_t fio_u8x128_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_sub`
```c
uint8_t fio_u8x128_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reduce_xor`
```c
uint8_t fio_u8x128_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x128_reshuffle`
```c
void fio_u8x128_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x128_sub`
```c
void fio_u8x128_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x128_xor`
```c
void fio_u8x128_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_add`
```c
void fio_u8x256_add(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_and`
```c
void fio_u8x256_and(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_mul`
```c
void fio_u8x256_mul(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_or`
```c
void fio_u8x256_or(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_add`
```c
uint8_t fio_u8x256_reduce_add(uint8_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_and`
```c
uint8_t fio_u8x256_reduce_and(uint8_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_max`
```c
uint8_t fio_u8x256_reduce_max(uint8_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_min`
```c
uint8_t fio_u8x256_reduce_min(uint8_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_mul`
```c
uint8_t fio_u8x256_reduce_mul(uint8_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_or`
```c
uint8_t fio_u8x256_reduce_or(uint8_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_sub`
```c
uint8_t fio_u8x256_reduce_sub(uint8_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reduce_xor`
```c
uint8_t fio_u8x256_reduce_xor(uint8_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u8x256_reshuffle`
```c
void fio_u8x256_reshuffle(uint8_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x256_sub`
```c
void fio_u8x256_sub(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u8x256_xor`
```c
void fio_u8x256_xor(uint8_t *dest, uint8_t *a, uint8_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_add`
```c
void fio_u16x2_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_and`
```c
void fio_u16x2_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_mul`
```c
void fio_u16x2_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_or`
```c
void fio_u16x2_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_add`
```c
uint16_t fio_u16x2_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_and`
```c
uint16_t fio_u16x2_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_max`
```c
uint16_t fio_u16x2_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_min`
```c
uint16_t fio_u16x2_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_mul`
```c
uint16_t fio_u16x2_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_or`
```c
uint16_t fio_u16x2_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_sub`
```c
uint16_t fio_u16x2_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reduce_xor`
```c
uint16_t fio_u16x2_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x2_reshuffle`
```c
void fio_u16x2_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x2_sub`
```c
void fio_u16x2_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x2_xor`
```c
void fio_u16x2_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_add`
```c
void fio_u16x4_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_and`
```c
void fio_u16x4_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_mul`
```c
void fio_u16x4_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_or`
```c
void fio_u16x4_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_add`
```c
uint16_t fio_u16x4_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_and`
```c
uint16_t fio_u16x4_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_max`
```c
uint16_t fio_u16x4_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_min`
```c
uint16_t fio_u16x4_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_mul`
```c
uint16_t fio_u16x4_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_or`
```c
uint16_t fio_u16x4_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_sub`
```c
uint16_t fio_u16x4_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reduce_xor`
```c
uint16_t fio_u16x4_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x4_reshuffle`
```c
void fio_u16x4_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x4_sub`
```c
void fio_u16x4_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x4_xor`
```c
void fio_u16x4_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_add`
```c
void fio_u16x8_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_and`
```c
void fio_u16x8_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_mul`
```c
void fio_u16x8_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_or`
```c
void fio_u16x8_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_add`
```c
uint16_t fio_u16x8_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_and`
```c
uint16_t fio_u16x8_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_max`
```c
uint16_t fio_u16x8_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_min`
```c
uint16_t fio_u16x8_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_mul`
```c
uint16_t fio_u16x8_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_or`
```c
uint16_t fio_u16x8_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_sub`
```c
uint16_t fio_u16x8_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reduce_xor`
```c
uint16_t fio_u16x8_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x8_reshuffle`
```c
void fio_u16x8_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x8_sub`
```c
void fio_u16x8_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x8_xor`
```c
void fio_u16x8_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_add`
```c
void fio_u16x16_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_and`
```c
void fio_u16x16_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_mul`
```c
void fio_u16x16_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_or`
```c
void fio_u16x16_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_add`
```c
uint16_t fio_u16x16_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_and`
```c
uint16_t fio_u16x16_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_max`
```c
uint16_t fio_u16x16_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_min`
```c
uint16_t fio_u16x16_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_mul`
```c
uint16_t fio_u16x16_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_or`
```c
uint16_t fio_u16x16_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_sub`
```c
uint16_t fio_u16x16_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reduce_xor`
```c
uint16_t fio_u16x16_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x16_reshuffle`
```c
void fio_u16x16_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x16_sub`
```c
void fio_u16x16_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x16_xor`
```c
void fio_u16x16_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_add`
```c
void fio_u16x32_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_and`
```c
void fio_u16x32_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_mul`
```c
void fio_u16x32_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_or`
```c
void fio_u16x32_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_add`
```c
uint16_t fio_u16x32_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_and`
```c
uint16_t fio_u16x32_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_max`
```c
uint16_t fio_u16x32_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_min`
```c
uint16_t fio_u16x32_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_mul`
```c
uint16_t fio_u16x32_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_or`
```c
uint16_t fio_u16x32_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_sub`
```c
uint16_t fio_u16x32_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reduce_xor`
```c
uint16_t fio_u16x32_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x32_reshuffle`
```c
void fio_u16x32_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x32_sub`
```c
void fio_u16x32_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x32_xor`
```c
void fio_u16x32_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_add`
```c
void fio_u16x64_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_and`
```c
void fio_u16x64_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_mul`
```c
void fio_u16x64_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_or`
```c
void fio_u16x64_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_add`
```c
uint16_t fio_u16x64_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_and`
```c
uint16_t fio_u16x64_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_max`
```c
uint16_t fio_u16x64_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_min`
```c
uint16_t fio_u16x64_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_mul`
```c
uint16_t fio_u16x64_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_or`
```c
uint16_t fio_u16x64_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_sub`
```c
uint16_t fio_u16x64_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reduce_xor`
```c
uint16_t fio_u16x64_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x64_reshuffle`
```c
void fio_u16x64_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x64_sub`
```c
void fio_u16x64_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x64_xor`
```c
void fio_u16x64_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_add`
```c
void fio_u16x128_add(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_and`
```c
void fio_u16x128_and(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_mul`
```c
void fio_u16x128_mul(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_or`
```c
void fio_u16x128_or(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_add`
```c
uint16_t fio_u16x128_reduce_add(uint16_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_and`
```c
uint16_t fio_u16x128_reduce_and(uint16_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_max`
```c
uint16_t fio_u16x128_reduce_max(uint16_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_min`
```c
uint16_t fio_u16x128_reduce_min(uint16_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_mul`
```c
uint16_t fio_u16x128_reduce_mul(uint16_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_or`
```c
uint16_t fio_u16x128_reduce_or(uint16_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_sub`
```c
uint16_t fio_u16x128_reduce_sub(uint16_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reduce_xor`
```c
uint16_t fio_u16x128_reduce_xor(uint16_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u16x128_reshuffle`
```c
void fio_u16x128_reshuffle(uint16_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u16x128_sub`
```c
void fio_u16x128_sub(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u16x128_xor`
```c
void fio_u16x128_xor(uint16_t *dest, uint16_t *a, uint16_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_add`
```c
void fio_u32x2_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_and`
```c
void fio_u32x2_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_mul`
```c
void fio_u32x2_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_or`
```c
void fio_u32x2_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_add`
```c
uint32_t fio_u32x2_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_and`
```c
uint32_t fio_u32x2_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_max`
```c
uint32_t fio_u32x2_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_min`
```c
uint32_t fio_u32x2_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_mul`
```c
uint32_t fio_u32x2_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_or`
```c
uint32_t fio_u32x2_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_sub`
```c
uint32_t fio_u32x2_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reduce_xor`
```c
uint32_t fio_u32x2_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x2_reshuffle`
```c
void fio_u32x2_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x2_sub`
```c
void fio_u32x2_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x2_xor`
```c
void fio_u32x2_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_add`
```c
void fio_u32x4_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_and`
```c
void fio_u32x4_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_mul`
```c
void fio_u32x4_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_or`
```c
void fio_u32x4_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_add`
```c
uint32_t fio_u32x4_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_and`
```c
uint32_t fio_u32x4_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_max`
```c
uint32_t fio_u32x4_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_min`
```c
uint32_t fio_u32x4_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_mul`
```c
uint32_t fio_u32x4_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_or`
```c
uint32_t fio_u32x4_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_sub`
```c
uint32_t fio_u32x4_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reduce_xor`
```c
uint32_t fio_u32x4_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x4_reshuffle`
```c
void fio_u32x4_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x4_sub`
```c
void fio_u32x4_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x4_xor`
```c
void fio_u32x4_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_add`
```c
void fio_u32x8_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_and`
```c
void fio_u32x8_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_mul`
```c
void fio_u32x8_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_or`
```c
void fio_u32x8_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_add`
```c
uint32_t fio_u32x8_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_and`
```c
uint32_t fio_u32x8_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_max`
```c
uint32_t fio_u32x8_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_min`
```c
uint32_t fio_u32x8_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_mul`
```c
uint32_t fio_u32x8_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_or`
```c
uint32_t fio_u32x8_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_sub`
```c
uint32_t fio_u32x8_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reduce_xor`
```c
uint32_t fio_u32x8_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x8_reshuffle`
```c
void fio_u32x8_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x8_sub`
```c
void fio_u32x8_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x8_xor`
```c
void fio_u32x8_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_add`
```c
void fio_u32x16_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_and`
```c
void fio_u32x16_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_mul`
```c
void fio_u32x16_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_or`
```c
void fio_u32x16_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_add`
```c
uint32_t fio_u32x16_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_and`
```c
uint32_t fio_u32x16_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_max`
```c
uint32_t fio_u32x16_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_min`
```c
uint32_t fio_u32x16_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_mul`
```c
uint32_t fio_u32x16_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_or`
```c
uint32_t fio_u32x16_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_sub`
```c
uint32_t fio_u32x16_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reduce_xor`
```c
uint32_t fio_u32x16_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x16_reshuffle`
```c
void fio_u32x16_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x16_sub`
```c
void fio_u32x16_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x16_xor`
```c
void fio_u32x16_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_add`
```c
void fio_u32x32_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_and`
```c
void fio_u32x32_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_mul`
```c
void fio_u32x32_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_or`
```c
void fio_u32x32_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_add`
```c
uint32_t fio_u32x32_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_and`
```c
uint32_t fio_u32x32_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_max`
```c
uint32_t fio_u32x32_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_min`
```c
uint32_t fio_u32x32_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_mul`
```c
uint32_t fio_u32x32_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_or`
```c
uint32_t fio_u32x32_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_sub`
```c
uint32_t fio_u32x32_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reduce_xor`
```c
uint32_t fio_u32x32_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x32_reshuffle`
```c
void fio_u32x32_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x32_sub`
```c
void fio_u32x32_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x32_xor`
```c
void fio_u32x32_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_add`
```c
void fio_u32x64_add(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_and`
```c
void fio_u32x64_and(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_mul`
```c
void fio_u32x64_mul(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_or`
```c
void fio_u32x64_or(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_add`
```c
uint32_t fio_u32x64_reduce_add(uint32_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_and`
```c
uint32_t fio_u32x64_reduce_and(uint32_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_max`
```c
uint32_t fio_u32x64_reduce_max(uint32_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_min`
```c
uint32_t fio_u32x64_reduce_min(uint32_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_mul`
```c
uint32_t fio_u32x64_reduce_mul(uint32_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_or`
```c
uint32_t fio_u32x64_reduce_or(uint32_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_sub`
```c
uint32_t fio_u32x64_reduce_sub(uint32_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reduce_xor`
```c
uint32_t fio_u32x64_reduce_xor(uint32_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u32x64_reshuffle`
```c
void fio_u32x64_reshuffle(uint32_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u32x64_sub`
```c
void fio_u32x64_sub(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u32x64_xor`
```c
void fio_u32x64_xor(uint32_t *dest, uint32_t *a, uint32_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_add`
```c
void fio_u64x2_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_and`
```c
void fio_u64x2_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_mul`
```c
void fio_u64x2_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_or`
```c
void fio_u64x2_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_add`
```c
uint64_t fio_u64x2_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_and`
```c
uint64_t fio_u64x2_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_max`
```c
uint64_t fio_u64x2_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_min`
```c
uint64_t fio_u64x2_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_mul`
```c
uint64_t fio_u64x2_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_or`
```c
uint64_t fio_u64x2_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_sub`
```c
uint64_t fio_u64x2_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reduce_xor`
```c
uint64_t fio_u64x2_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x2_reshuffle`
```c
void fio_u64x2_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x2_sub`
```c
void fio_u64x2_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x2_xor`
```c
void fio_u64x2_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_add`
```c
void fio_u64x4_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_and`
```c
void fio_u64x4_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_mul`
```c
void fio_u64x4_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_or`
```c
void fio_u64x4_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_add`
```c
uint64_t fio_u64x4_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_and`
```c
uint64_t fio_u64x4_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_max`
```c
uint64_t fio_u64x4_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_min`
```c
uint64_t fio_u64x4_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_mul`
```c
uint64_t fio_u64x4_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_or`
```c
uint64_t fio_u64x4_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_sub`
```c
uint64_t fio_u64x4_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reduce_xor`
```c
uint64_t fio_u64x4_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x4_reshuffle`
```c
void fio_u64x4_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x4_sub`
```c
void fio_u64x4_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x4_xor`
```c
void fio_u64x4_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_add`
```c
void fio_u64x8_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_and`
```c
void fio_u64x8_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_mul`
```c
void fio_u64x8_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_or`
```c
void fio_u64x8_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_add`
```c
uint64_t fio_u64x8_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_and`
```c
uint64_t fio_u64x8_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_max`
```c
uint64_t fio_u64x8_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_min`
```c
uint64_t fio_u64x8_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_mul`
```c
uint64_t fio_u64x8_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_or`
```c
uint64_t fio_u64x8_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_sub`
```c
uint64_t fio_u64x8_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reduce_xor`
```c
uint64_t fio_u64x8_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x8_reshuffle`
```c
void fio_u64x8_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x8_sub`
```c
void fio_u64x8_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x8_xor`
```c
void fio_u64x8_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_add`
```c
void fio_u64x16_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_and`
```c
void fio_u64x16_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_mul`
```c
void fio_u64x16_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_or`
```c
void fio_u64x16_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_add`
```c
uint64_t fio_u64x16_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_and`
```c
uint64_t fio_u64x16_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_max`
```c
uint64_t fio_u64x16_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_min`
```c
uint64_t fio_u64x16_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_mul`
```c
uint64_t fio_u64x16_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_or`
```c
uint64_t fio_u64x16_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_sub`
```c
uint64_t fio_u64x16_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reduce_xor`
```c
uint64_t fio_u64x16_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x16_reshuffle`
```c
void fio_u64x16_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x16_sub`
```c
void fio_u64x16_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x16_xor`
```c
void fio_u64x16_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_add`
```c
void fio_u64x32_add(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_and`
```c
void fio_u64x32_and(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a & b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_mul`
```c
void fio_u64x32_mul(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_or`
```c
void fio_u64x32_or(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a | b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_add`
```c
uint64_t fio_u64x32_reduce_add(uint64_t *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_and`
```c
uint64_t fio_u64x32_reduce_and(uint64_t *v)
```
Performs `and` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_max`
```c
uint64_t fio_u64x32_reduce_max(uint64_t *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_min`
```c
uint64_t fio_u64x32_reduce_min(uint64_t *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_mul`
```c
uint64_t fio_u64x32_reduce_mul(uint64_t *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_or`
```c
uint64_t fio_u64x32_reduce_or(uint64_t *v)
```
Performs `or` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_sub`
```c
uint64_t fio_u64x32_reduce_sub(uint64_t *v)
```
Performs `sub` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reduce_xor`
```c
uint64_t fio_u64x32_reduce_xor(uint64_t *v)
```
Performs `xor` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_u64x32_reshuffle`
```c
void fio_u64x32_reshuffle(uint64_t *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u64x32_sub`
```c
void fio_u64x32_sub(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a - b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_u64x32_xor`
```c
void fio_u64x32_xor(uint64_t *dest, uint64_t *a, uint64_t *b)
```
Performs operation `a ^ b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_add`
```c
void fio_floatx2_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_mul`
```c
void fio_floatx2_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_add`
```c
float fio_floatx2_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_max`
```c
float fio_floatx2_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_min`
```c
float fio_floatx2_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx2_reduce_mul`
```c
float fio_floatx2_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx2_reshuffle`
```c
void fio_floatx2_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx4_add`
```c
void fio_floatx4_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx4_mul`
```c
void fio_floatx4_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_add`
```c
float fio_floatx4_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_max`
```c
float fio_floatx4_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_min`
```c
float fio_floatx4_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx4_reduce_mul`
```c
float fio_floatx4_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx4_reshuffle`
```c
void fio_floatx4_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx8_add`
```c
void fio_floatx8_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx8_mul`
```c
void fio_floatx8_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_add`
```c
float fio_floatx8_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_max`
```c
float fio_floatx8_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_min`
```c
float fio_floatx8_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx8_reduce_mul`
```c
float fio_floatx8_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx8_reshuffle`
```c
void fio_floatx8_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx16_add`
```c
void fio_floatx16_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx16_mul`
```c
void fio_floatx16_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_add`
```c
float fio_floatx16_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_max`
```c
float fio_floatx16_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_min`
```c
float fio_floatx16_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx16_reduce_mul`
```c
float fio_floatx16_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx16_reshuffle`
```c
void fio_floatx16_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx32_add`
```c
void fio_floatx32_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx32_mul`
```c
void fio_floatx32_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_add`
```c
float fio_floatx32_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_max`
```c
float fio_floatx32_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_min`
```c
float fio_floatx32_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx32_reduce_mul`
```c
float fio_floatx32_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx32_reshuffle`
```c
void fio_floatx32_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_floatx64_add`
```c
void fio_floatx64_add(float *dest, float *a, float *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx64_mul`
```c
void fio_floatx64_mul(float *dest, float *a, float *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_add`
```c
float fio_floatx64_reduce_add(float *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_max`
```c
float fio_floatx64_reduce_max(float *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_min`
```c
float fio_floatx64_reduce_min(float *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_floatx64_reduce_mul`
```c
float fio_floatx64_reduce_mul(float *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_floatx64_reshuffle`
```c
void fio_floatx64_reshuffle(float *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx2_add`
```c
void fio_dblx2_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx2_mul`
```c
void fio_dblx2_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_add`
```c
double fio_dblx2_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_max`
```c
double fio_dblx2_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_min`
```c
double fio_dblx2_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx2_reduce_mul`
```c
double fio_dblx2_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx2_reshuffle`
```c
void fio_dblx2_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx4_add`
```c
void fio_dblx4_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx4_mul`
```c
void fio_dblx4_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_add`
```c
double fio_dblx4_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_max`
```c
double fio_dblx4_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_min`
```c
double fio_dblx4_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx4_reduce_mul`
```c
double fio_dblx4_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx4_reshuffle`
```c
void fio_dblx4_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx8_add`
```c
void fio_dblx8_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx8_mul`
```c
void fio_dblx8_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_add`
```c
double fio_dblx8_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_max`
```c
double fio_dblx8_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_min`
```c
double fio_dblx8_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx8_reduce_mul`
```c
double fio_dblx8_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx8_reshuffle`
```c
void fio_dblx8_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx16_add`
```c
void fio_dblx16_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx16_mul`
```c
void fio_dblx16_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_add`
```c
double fio_dblx16_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_max`
```c
double fio_dblx16_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_min`
```c
double fio_dblx16_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx16_reduce_mul`
```c
double fio_dblx16_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx16_reshuffle`
```c
void fio_dblx16_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_dblx32_add`
```c
void fio_dblx32_add(double *dest, double *a, double *b)
```
Performs operation `a + b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx32_mul`
```c
void fio_dblx32_mul(double *dest, double *a, double *b)
```
Performs operation `a * b`, storing the result in `dest`.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_add`
```c
double fio_dblx32_reduce_add(double *v)
```
Performs `add` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_max`
```c
double fio_dblx32_reduce_max(double *v)
```
Returns the maximum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_min`
```c
double fio_dblx32_reduce_min(double *v)
```
Returns the minimum value in a vector.
_Symbol type:_ `function`
#### `fio_dblx32_reduce_mul`
```c
double fio_dblx32_reduce_mul(double *v)
```
Performs `mul` in order along the vector, returning the result.
_Symbol type:_ `function`
#### `fio_dblx32_reshuffle`
```c
void fio_dblx32_reshuffle(double *v, uint8_t *indx)
```
Reorders (shuffles) the vectors using the indexes in `indx`.
_Symbol type:_ `function`
#### `fio_u8x4_reshuffle`
```c
#define fio_u8x4_reshuffle(v, ...) fio_u8x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x8_reshuffle`
```c
#define fio_u8x8_reshuffle(v, ...) fio_u8x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x16_reshuffle`
```c
#define fio_u8x16_reshuffle(v, ...) fio_u8x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x32_reshuffle`
```c
#define fio_u8x32_reshuffle(v, ...) fio_u8x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x64_reshuffle`
```c
#define fio_u8x64_reshuffle(v, ...) fio_u8x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x128_reshuffle`
```c
#define fio_u8x128_reshuffle(v, ...) fio_u8x128_reshuffle(v, (uint8_t[128]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u8x256_reshuffle`
```c
#define fio_u8x256_reshuffle(v, ...) fio_u8x256_reshuffle(v, (uint8_t[256]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x2_reshuffle`
```c
#define fio_u16x2_reshuffle(v, ...) fio_u16x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x4_reshuffle`
```c
#define fio_u16x4_reshuffle(v, ...) fio_u16x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x8_reshuffle`
```c
#define fio_u16x8_reshuffle(v, ...) fio_u16x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x16_reshuffle`
```c
#define fio_u16x16_reshuffle(v, ...) fio_u16x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x32_reshuffle`
```c
#define fio_u16x32_reshuffle(v, ...) fio_u16x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x64_reshuffle`
```c
#define fio_u16x64_reshuffle(v, ...) fio_u16x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u16x128_reshuffle`
```c
#define fio_u16x128_reshuffle(v,...) fio_u16x128_reshuffle(v, (uint8_t[128]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x2_reshuffle`
```c
#define fio_u32x2_reshuffle(v, ...) fio_u32x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x4_reshuffle`
```c
#define fio_u32x4_reshuffle(v, ...) fio_u32x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x8_reshuffle`
```c
#define fio_u32x8_reshuffle(v, ...) fio_u32x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x16_reshuffle`
```c
#define fio_u32x16_reshuffle(v, ...) fio_u32x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x32_reshuffle`
```c
#define fio_u32x32_reshuffle(v, ...) fio_u32x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u32x64_reshuffle`
```c
#define fio_u32x64_reshuffle(v, ...) fio_u32x64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x2_reshuffle`
```c
#define fio_u64x2_reshuffle(v, ...) fio_u64x2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x4_reshuffle`
```c
#define fio_u64x4_reshuffle(v, ...) fio_u64x4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x8_reshuffle`
```c
#define fio_u64x8_reshuffle(v, ...) fio_u64x8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x16_reshuffle`
```c
#define fio_u64x16_reshuffle(v, ...) fio_u64x16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u64x32_reshuffle`
```c
#define fio_u64x32_reshuffle(v, ...) fio_u64x32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx2_reshuffle`
```c
#define fio_floatx2_reshuffle(v, ...) fio_floatx2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx4_reshuffle`
```c
#define fio_floatx4_reshuffle(v, ...) fio_floatx4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx8_reshuffle`
```c
#define fio_floatx8_reshuffle(v, ...) fio_floatx8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx16_reshuffle`
```c
#define fio_floatx16_reshuffle(v, ...) fio_floatx16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx32_reshuffle`
```c
#define fio_floatx32_reshuffle(v, ...) fio_floatx32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_floatx64_reshuffle`
```c
#define fio_floatx64_reshuffle(v, ...) fio_floatx64_reshuffle(v, (uint8_t[64]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx2_reshuffle`
```c
#define fio_dblx2_reshuffle(v, ...) fio_dblx2_reshuffle(v, (uint8_t[2]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx4_reshuffle`
```c
#define fio_dblx4_reshuffle(v, ...) fio_dblx4_reshuffle(v, (uint8_t[4]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx8_reshuffle`
```c
#define fio_dblx8_reshuffle(v, ...) fio_dblx8_reshuffle(v, (uint8_t[8]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx16_reshuffle`
```c
#define fio_dblx16_reshuffle(v, ...) fio_dblx16_reshuffle(v, (uint8_t[16]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_dblx32_reshuffle`
```c
#define fio_dblx32_reshuffle(v, ...) fio_dblx32_reshuffle(v, (uint8_t[32]){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ct_true`
```c
inline FIO_CONST uintmax_t fio_ct_true(uintmax_t cond)
```
Returns 1 if the expression is true (input isn't zero).
_Symbol type:_ `function`
#### `fio_ct_false`
```c
inline FIO_CONST uintmax_t fio_ct_false(uintmax_t cond)
```
Returns 1 if the expression is false (input is zero).
_Symbol type:_ `function`
#### `fio_ct_if_bool`
```c
inline FIO_CONST uintmax_t fio_ct_if_bool(uintmax_t cond, uintmax_t a, uintmax_t b)
```
Returns `a` if `cond` is boolean and true, returns b otherwise.
_Symbol type:_ `function`
#### `fio_ct_if`
```c
inline FIO_CONST uintmax_t fio_ct_if(uintmax_t cond, uintmax_t a, uintmax_t b)
```
Returns `a` if `cond` isn't zero (uses fio_ct_true), returns b otherwise.
_Symbol type:_ `function`
#### `fio_ct_max`
```c
inline FIO_CONST intmax_t fio_ct_max(intmax_t a_, intmax_t b_)
```
Returns `a` if a >= `b`.
_Symbol type:_ `function`
#### `fio_ct_min`
```c
inline FIO_CONST intmax_t fio_ct_min(intmax_t a_, intmax_t b_)
```
Returns `a` if a >= `b`.
_Symbol type:_ `function`
#### `fio_ct_abs`
```c
inline FIO_CONST uintmax_t fio_ct_abs(intmax_t i_)
```
Returns absolute value.
_Symbol type:_ `function`
#### `fio_ct_tolower`
```c
inline char fio_ct_tolower(char c)
```
_Symbol type:_ `function`
#### `fio_ct_mux32`
```c
inline FIO_CONST uint32_t fio_ct_mux32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "choose": for each bit, if x is set, return y's bit, else z's bit.
Formula: (x & y) ^ (~x & z) -or equivalently- z ^ (x & (y ^ z))
Used in: SHA-1 (Ch), SHA-256 (Ch), SHA-512 (Ch), AES, etc.
_Symbol type:_ `function`
#### `fio_ct_mux64`
```c
inline FIO_CONST uint64_t fio_ct_mux64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of bitwise choose/mux.
_Symbol type:_ `function`
#### `fio_ct_maj32`
```c
inline FIO_CONST uint32_t fio_ct_maj32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "majority": for each bit position, return 1 if 2+ inputs have 1.
Formula: (x & y) ^ (x & z) ^ (y & z) -or- (x & y) | (z & (x | y))
Used in: SHA-1 (Maj), SHA-256 (Maj), SHA-512 (Maj), etc.
_Symbol type:_ `function`
#### `fio_ct_maj64`
```c
inline FIO_CONST uint64_t fio_ct_maj64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of bitwise majority.
_Symbol type:_ `function`
#### `fio_ct_xor3_32`
```c
inline FIO_CONST uint32_t fio_ct_xor3_32(uint32_t x, uint32_t y, uint32_t z)
```
Bitwise "parity": XOR of all three inputs (1 if odd number of 1s).
Formula: x ^ y ^ z
Used in: SHA-1 (Parity function for rounds 20-39 and 60-79)
_Symbol type:_ `function`
#### `fio_ct_xor3_64`
```c
inline FIO_CONST uint64_t fio_ct_xor3_64(uint64_t x, uint64_t y, uint64_t z)
```
64-bit version of 3-way XOR.
_Symbol type:_ `function`
#### `fio_ct_is_eq`
```c
FIO_SFUNC _Bool fio_ct_is_eq(const void *a_, const void *b_, size_t bytes)
```
A timing attack resistant memory comparison function.
_Symbol type:_ `function`
#### `fio_secure_zero`
```c
inline void fio_secure_zero(void *a_, size_t bytes)
```
A timing attack resistant memory zeroing function.
_Symbol type:_ `function`
#### `fio_lrot8`
```c
#define fio_lrot8(i, bits) __builtin_rotateleft8(i, bits)
```
8Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot16`
```c
#define fio_lrot16(i, bits) __builtin_rotateleft16(i, bits)
```
16Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot32`
```c
#define fio_lrot32(i, bits) __builtin_rotateleft32(i, bits)
```
32Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot64`
```c
#define fio_lrot64(i, bits) __builtin_rotateleft64(i, bits)
```
64Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot8`
```c
#define fio_rrot8(i, bits) __builtin_rotateright8(i, bits)
```
8Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot8`
```c
inline FIO_CONST uint8_t fio_rrot8(uint8_t i, uint8_t bits)
```
8Bit right rotation, inlined.
_Symbol type:_ `function`
#### `fio_rrot16`
```c
#define fio_rrot16(i, bits) __builtin_rotateright16(i, bits)
```
16Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot32`
```c
#define fio_rrot32(i, bits) __builtin_rotateright32(i, bits)
```
32Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot64`
```c
#define fio_rrot64(i, bits) __builtin_rotateright64(i, bits)
```
64Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot128`
```c
#define fio_lrot128(i, bits) __builtin_rotateleft128(i, bits)
```
128Bit left rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_rrot128`
```c
#define fio_rrot128(i, bits) __builtin_rotateright128(i, bits)
```
128Bit right rotation, inlined.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lrot128`
```c
inline FIO_CONST __uint128_t fio_lrot128(__uint128_t i, uint8_t bits)
```
128Bit left rotation, inlined.
_Symbol type:_ `function`
#### `fio_rrot128`
```c
inline FIO_CONST __uint128_t fio_rrot128(__uint128_t i, uint8_t bits)
```
128Bit right rotation, inlined.
_Symbol type:_ `function`
#### `fio_frot16`
```c
#define fio_frot16 fio_rrot16
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_frot32`
```c
#define fio_frot32 fio_rrot32
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_frot64`
```c
#define fio_frot64 fio_rrot64
```
Rotates the bits Forwards (endian specific).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_xor_rrot3_32`
```c
inline FIO_CONST uint32_t fio_xor_rrot3_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c)
```
XOR of three right rotations: ROTR(x,a) ^ ROTR(x,b) ^ ROTR(x,c)
Common in SHA-256/SHA-512 Sigma functions.
_Symbol type:_ `function`
#### `fio_xor_rrot3_64`
```c
inline FIO_CONST uint64_t fio_xor_rrot3_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c)
```
64-bit version of triple rotation XOR.
_Symbol type:_ `function`
#### `fio_xor_rrot2_shr_32`
```c
inline FIO_CONST uint32_t fio_xor_rrot2_shr_32(uint32_t x, uint8_t a, uint8_t b, uint8_t c)
```
XOR of two right rotations and a right shift: ROTR(x,a) ^ ROTR(x,b) ^
SHR(x,c) Common in SHA-256/SHA-512 sigma (lowercase) functions for message
schedule.
_Symbol type:_ `function`
#### `fio_xor_rrot2_shr_64`
```c
inline FIO_CONST uint64_t fio_xor_rrot2_shr_64(uint64_t x, uint8_t a, uint8_t b, uint8_t c)
```
64-bit version of double rotation + shift XOR.
_Symbol type:_ `function`
#### `fio_xmask`
```c
inline void fio_xmask(void *buf_, size_t len, uint64_t mask)
```
Masks data using a persistent 64 bit mask.
When the buffer's memory is aligned, the function may perform significantly
better.
_Symbol type:_ `function`
#### `fio_xmask_cpy`
```c
inline void fio_xmask_cpy(char *restrict dest, const char *src, size_t len, uint64_t mask)
```
Masks data using a persistent 64 bit mask.
When the buffer's memory is aligned, the function may perform significantly
better.
_Symbol type:_ `function`
#### `fio_popcount`
```c
#define fio_popcount(n) __builtin_popcountll(n)
```
performs a `popcount` operation to count the set bits.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_hemming_dist`
```c
#define fio_hemming_dist(n1, n2) fio_popcount(((uint64_t)(n1) ^ (uint64_t)(n2)))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lsb_index_unsafe`
```c
FIO_SFUNC FIO_CONST size_t fio_lsb_index_unsafe(uint64_t i)
```
Returns the index of the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_msb_index_unsafe`
```c
FIO_SFUNC FIO_CONST size_t fio_msb_index_unsafe(uint64_t i)
```
Returns the index of the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_has_zero_byte32`
```c
inline FIO_CONST uint32_t fio_has_zero_byte32(uint32_t row)
```
Detects a byte where no bits are set (0) within a 4 byte vector.
The zero byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_byte32`
```c
inline FIO_CONST uint32_t fio_has_byte32(uint32_t row, uint8_t byte)
```
Detects if `byte` exists within a 4 byte vector.
The requested byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_full_byte32`
```c
inline FIO_CONST uint32_t fio_has_full_byte32(uint32_t row)
```
Detects a byte where all the bits are set (255) within a 4 byte vector.
The full byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_zero_byte64`
```c
inline FIO_CONST uint64_t fio_has_zero_byte64(uint64_t row)
```
Detects a byte where no bits are set (0) within an 8 byte vector.
The zero byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_zero_byte_alt64`
```c
inline FIO_CONST uint64_t fio_has_zero_byte_alt64(uint64_t row)
```
Detects a byte where no bits are set (0) within an 8 byte vector.
This variation should NOT be used to build a bitmap, but May be used to
detect the first occurrence.
_Symbol type:_ `function`
#### `fio_has_byte64`
```c
inline FIO_CONST uint64_t fio_has_byte64(uint64_t row, uint8_t byte)
```
Detects if `byte` exists within an 8 byte vector.
The requested byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_full_byte64`
```c
inline FIO_CONST uint64_t fio_has_full_byte64(uint64_t row)
```
Detects a byte where all the bits are set (255) within an 8 byte vector.
The full byte will be be set to 0x80, all other bytes will be 0x0.
_Symbol type:_ `function`
#### `fio_has_byte2bitmap`
```c
inline FIO_CONST uint64_t fio_has_byte2bitmap(uint64_t result)
```
Converts a `fio_has_byteX` result to a bitmap.
_Symbol type:_ `function`
#### `fio_bits_lsb`
```c
inline FIO_CONST uint64_t fio_bits_lsb(uint64_t i)
```
Isolates the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_bits_msb`
```c
inline FIO_CONST uint64_t fio_bits_msb(uint64_t i)
```
Isolates the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_bits_msb_index`
```c
inline FIO_CONST size_t fio_bits_msb_index(uint64_t i)
```
Returns the index of the most significant (highest) bit.
_Symbol type:_ `function`
#### `fio_bits_lsb_index`
```c
inline FIO_CONST size_t fio_bits_lsb_index(uint64_t i)
```
Returns the index of the least significant (lowest) bit.
_Symbol type:_ `function`
#### `fio_bit_get`
```c
inline uint8_t fio_bit_get(void *map, size_t bit)
```
Gets the state of a bit in a bitmap.
_Symbol type:_ `function`
#### `fio_bit_set`
```c
inline void fio_bit_set(void *map, size_t bit)
```
Sets the a bit in a bitmap (sets to 1).
_Symbol type:_ `function`
#### `fio_bit_unset`
```c
inline void fio_bit_unset(void *map, size_t bit)
```
Unsets the a bit in a bitmap (sets to 0).
_Symbol type:_ `function`
#### `fio_bit_flip`
```c
inline void fio_bit_flip(void *map, size_t bit)
```
Flips the a bit in a bitmap (sets to 0 if 1, sets to 1 if 0).
_Symbol type:_ `function`
#### `fio_math_mod_mul64`
```c
inline uint64_t fio_math_mod_mul64(uint64_t a, uint64_t b, uint64_t mod)
```
Perform modular multiplication for numbers with up to 64 bits.
_Symbol type:_ `function`
#### `fio_math_mod_expo64`
```c
inline uint64_t fio_math_mod_expo64(uint64_t base, uint64_t exp, uint64_t mod)
```
Perform modular exponentiation
_Symbol type:_ `function`
#### `fio_math_is_uprime`
```c
FIO_SFUNC bool fio_math_is_uprime(uint64_t n)
```
Tests if an unsigned 64 bit number is (probably) a prime.
For numbers up to 1023 this is deterministic.
_Symbol type:_ `function`
#### `fio_math_is_iprime`
```c
inline bool fio_math_is_iprime(int64_t n)
```
Tests if the absolute value of a signed 64 bit number is (probably) a prime.
For numbers up to 1023 this is deterministic.
_Symbol type:_ `function`
#### `fio_math_addc64`
```c
FIO_MIFN uint64_t fio_math_addc64(uint64_t a, uint64_t b, uint64_t carry_in, uint64_t *carry_out)
```
Add with carry.
_Symbol type:_ `function`
#### `fio_math_add`
```c
FIO_MIFN bool fio_math_add(uint64_t *dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision ADD for `len` 64 bit words a + b. Returns the carry.
_Symbol type:_ `function`
#### `fio_math_addc128`
```c
FIO_MIFN __uint128_t fio_math_addc128(const __uint128_t a, const __uint128_t b, bool carry_in, bool *carry_out)
```
Multi-precision ADD for `bits` long a + b. Returns the carry.
_Symbol type:_ `function`
#### `fio_math_add2`
```c
FIO_MIFN bool fio_math_add2(__uint128_t *dest, const __uint128_t *a, const __uint128_t *b, const size_t len)
```
_Symbol type:_ `function`
#### `fio_math_subc64`
```c
FIO_MIFN uint64_t fio_math_subc64(uint64_t a, uint64_t b, uint64_t borrow_in, uint64_t *borrow_out)
```
Subtract with borrow.
_Symbol type:_ `function`
#### `fio_math_sub`
```c
FIO_MIFN uint64_t fio_math_sub(uint64_t *dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision SUB for `len` 64 bit words a + b. Returns the borrow.
_Symbol type:_ `function`
#### `fio_math_mulc64`
```c
FIO_MIFN uint64_t fio_math_mulc64(uint64_t a, uint64_t b, uint64_t *carry_out)
```
Multiply with carry out.
_Symbol type:_ `function`
#### `fio_math_mul64_fold`
```c
FIO_MIFN uint64_t fio_math_mul64_fold(uint64_t a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_math_mul`
```c
inline void fio_math_mul(uint64_t *restrict dest, const uint64_t *a, const uint64_t *b, const size_t len)
```
Multi-precision MUL for `len` 64 bit words.
`dest` must be `len * 2` long to hold the result.
`a` and `b` must be of equal `len`.
_Symbol type:_ `function`
#### `fio_u128_init8`
```c
#define fio_u128_init8(...) ((fio_u128){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init16`
```c
#define fio_u128_init16(...) ((fio_u128){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init32`
```c
#define fio_u128_init32(...) ((fio_u128){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_init64`
```c
#define fio_u128_init64(...) ((fio_u128){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init8`
```c
#define fio_u256_init8(...) ((fio_u256){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init16`
```c
#define fio_u256_init16(...) ((fio_u256){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init32`
```c
#define fio_u256_init32(...) ((fio_u256){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u256_init64`
```c
#define fio_u256_init64(...) ((fio_u256){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init8`
```c
#define fio_u512_init8(...) ((fio_u512){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init16`
```c
#define fio_u512_init16(...) ((fio_u512){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init32`
```c
#define fio_u512_init32(...) ((fio_u512){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u512_init64`
```c
#define fio_u512_init64(...) ((fio_u512){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init8`
```c
#define fio_u1024_init8(...) ((fio_u1024){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init16`
```c
#define fio_u1024_init16(...) ((fio_u1024){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init32`
```c
#define fio_u1024_init32(...) ((fio_u1024){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u1024_init64`
```c
#define fio_u1024_init64(...) ((fio_u1024){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init8`
```c
#define fio_u2048_init8(...) ((fio_u2048){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init16`
```c
#define fio_u2048_init16(...) ((fio_u2048){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init32`
```c
#define fio_u2048_init32(...) ((fio_u2048){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u2048_init64`
```c
#define fio_u2048_init64(...) ((fio_u2048){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init8`
```c
#define fio_u4096_init8(...) ((fio_u4096){.u8 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init16`
```c
#define fio_u4096_init16(...) ((fio_u4096){.u16 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init32`
```c
#define fio_u4096_init32(...) ((fio_u4096){.u32 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u4096_init64`
```c
#define fio_u4096_init64(...) ((fio_u4096){.u64 = {__VA_ARGS__}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_u128_bswap16`
```c
fio_u128 fio_u128_bswap16(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_bswap32`
```c
fio_u128 fio_u128_bswap32(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_bswap64`
```c
fio_u128 fio_u128_bswap64(fio_u128 a)
```
_Symbol type:_ `function`
#### `fio_u128_load`
```c
fio_u128 fio_u128_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u128_load_be16`
```c
fio_u128 fio_u128_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_be32`
```c
fio_u128 fio_u128_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_be64`
```c
fio_u128 fio_u128_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le16`
```c
fio_u128 fio_u128_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le32`
```c
fio_u128 fio_u128_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_load_le64`
```c
fio_u128 fio_u128_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u128_store`
```c
void fio_u128_store(void *buf, const fio_u128 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u256_bswap16`
```c
fio_u256 fio_u256_bswap16(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_bswap32`
```c
fio_u256 fio_u256_bswap32(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_bswap64`
```c
fio_u256 fio_u256_bswap64(fio_u256 a)
```
_Symbol type:_ `function`
#### `fio_u256_load`
```c
fio_u256 fio_u256_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u256_load_be16`
```c
fio_u256 fio_u256_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_be32`
```c
fio_u256 fio_u256_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_be64`
```c
fio_u256 fio_u256_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le16`
```c
fio_u256 fio_u256_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le32`
```c
fio_u256 fio_u256_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_load_le64`
```c
fio_u256 fio_u256_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u256_store`
```c
void fio_u256_store(void *buf, const fio_u256 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u512_bswap16`
```c
fio_u512 fio_u512_bswap16(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_bswap32`
```c
fio_u512 fio_u512_bswap32(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_bswap64`
```c
fio_u512 fio_u512_bswap64(fio_u512 a)
```
_Symbol type:_ `function`
#### `fio_u512_load`
```c
fio_u512 fio_u512_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u512_load_be16`
```c
fio_u512 fio_u512_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_be32`
```c
fio_u512 fio_u512_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_be64`
```c
fio_u512 fio_u512_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le16`
```c
fio_u512 fio_u512_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le32`
```c
fio_u512 fio_u512_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_load_le64`
```c
fio_u512 fio_u512_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u512_store`
```c
void fio_u512_store(void *buf, const fio_u512 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u1024_bswap16`
```c
fio_u1024 fio_u1024_bswap16(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_bswap32`
```c
fio_u1024 fio_u1024_bswap32(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_bswap64`
```c
fio_u1024 fio_u1024_bswap64(fio_u1024 a)
```
_Symbol type:_ `function`
#### `fio_u1024_load`
```c
fio_u1024 fio_u1024_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u1024_load_be16`
```c
fio_u1024 fio_u1024_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_be32`
```c
fio_u1024 fio_u1024_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_be64`
```c
fio_u1024 fio_u1024_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le16`
```c
fio_u1024 fio_u1024_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le32`
```c
fio_u1024 fio_u1024_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_load_le64`
```c
fio_u1024 fio_u1024_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u1024_store`
```c
void fio_u1024_store(void *buf, const fio_u1024 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u2048_bswap16`
```c
fio_u2048 fio_u2048_bswap16(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_bswap32`
```c
fio_u2048 fio_u2048_bswap32(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_bswap64`
```c
fio_u2048 fio_u2048_bswap64(fio_u2048 a)
```
_Symbol type:_ `function`
#### `fio_u2048_load`
```c
fio_u2048 fio_u2048_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u2048_load_be16`
```c
fio_u2048 fio_u2048_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_be32`
```c
fio_u2048 fio_u2048_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_be64`
```c
fio_u2048 fio_u2048_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le16`
```c
fio_u2048 fio_u2048_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le32`
```c
fio_u2048 fio_u2048_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_load_le64`
```c
fio_u2048 fio_u2048_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u2048_store`
```c
void fio_u2048_store(void *buf, const fio_u2048 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u4096_bswap16`
```c
fio_u4096 fio_u4096_bswap16(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_bswap32`
```c
fio_u4096 fio_u4096_bswap32(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_bswap64`
```c
fio_u4096 fio_u4096_bswap64(fio_u4096 a)
```
_Symbol type:_ `function`
#### `fio_u4096_load`
```c
fio_u4096 fio_u4096_load(const void *buf)
```
Loads from memory using local-endian.
_Symbol type:_ `function`
#### `fio_u4096_load_be16`
```c
fio_u4096 fio_u4096_load_be16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_be32`
```c
fio_u4096 fio_u4096_load_be32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_be64`
```c
fio_u4096 fio_u4096_load_be64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le16`
```c
fio_u4096 fio_u4096_load_le16(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le32`
```c
fio_u4096 fio_u4096_load_le32(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_load_le64`
```c
fio_u4096 fio_u4096_load_le64(const void *buf)
```
_Symbol type:_ `function`
#### `fio_u4096_store`
```c
void fio_u4096_store(void *buf, const fio_u4096 a)
```
Stores to memory using local-endian.
_Symbol type:_ `function`
#### `fio_u128_3xor`
```c
void fio_u128_3xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_3xor32`
```c
void fio_u128_3xor32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_3xor64`
```c
void fio_u128_3xor64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_add16`
```c
void fio_u128_add16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add32`
```c
void fio_u128_add32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add64`
```c
void fio_u128_add64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_add8`
```c
void fio_u128_add8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and`
```c
void fio_u128_and(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and16`
```c
void fio_u128_and16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and32`
```c
void fio_u128_and32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and64`
```c
void fio_u128_and64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_and8`
```c
void fio_u128_and8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd16`
```c
void fio_u128_cadd16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd32`
```c
void fio_u128_cadd32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd64`
```c
void fio_u128_cadd64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cadd8`
```c
void fio_u128_cadd8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand16`
```c
void fio_u128_cand16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand32`
```c
void fio_u128_cand32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand64`
```c
void fio_u128_cand64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cand8`
```c
void fio_u128_cand8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_clrot16`
```c
void fio_u128_clrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot32`
```c
void fio_u128_clrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot64`
```c
void fio_u128_clrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_clrot8`
```c
void fio_u128_clrot8(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_cmul16`
```c
void fio_u128_cmul16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul32`
```c
void fio_u128_cmul32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul64`
```c
void fio_u128_cmul64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cmul8`
```c
void fio_u128_cmul8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor16`
```c
void fio_u128_cor16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor32`
```c
void fio_u128_cor32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor64`
```c
void fio_u128_cor64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cor8`
```c
void fio_u128_cor8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_crrot16`
```c
void fio_u128_crrot16(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot32`
```c
void fio_u128_crrot32(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot64`
```c
void fio_u128_crrot64(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_crrot8`
```c
void fio_u128_crrot8(fio_u128 *target, const fio_u128 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u128_csub16`
```c
void fio_u128_csub16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub32`
```c
void fio_u128_csub32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub64`
```c
void fio_u128_csub64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_csub8`
```c
void fio_u128_csub8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_ct_swap_if`
```c
void fio_u128_ct_swap_if(bool cond, fio_u128 *restrict a, fio_u128 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor16`
```c
void fio_u128_cxor16(fio_u128 *target, const fio_u128 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor32`
```c
void fio_u128_cxor32(fio_u128 *target, const fio_u128 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor64`
```c
void fio_u128_cxor64(fio_u128 *target, const fio_u128 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u128_cxor8`
```c
void fio_u128_cxor8(fio_u128 *target, const fio_u128 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u128_inv`
```c
void fio_u128_inv(fio_u128 *target, const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_is_eq`
```c
bool fio_u128_is_eq(const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_lrot16`
```c
void fio_u128_lrot16(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot32`
```c
void fio_u128_lrot32(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot64`
```c
void fio_u128_lrot64(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_lrot8`
```c
void fio_u128_lrot8(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_maj`
```c
void fio_u128_maj(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_maj32`
```c
void fio_u128_maj32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_maj64`
```c
void fio_u128_maj64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mul16`
```c
void fio_u128_mul16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul32`
```c
void fio_u128_mul32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul64`
```c
void fio_u128_mul64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mul8`
```c
void fio_u128_mul8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_mux`
```c
void fio_u128_mux(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mux32`
```c
void fio_u128_mux32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_mux64`
```c
void fio_u128_mux64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b, const fio_u128 *c)
```
_Symbol type:_ `function`
#### `fio_u128_or`
```c
void fio_u128_or(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or16`
```c
void fio_u128_or16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or32`
```c
void fio_u128_or32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or64`
```c
void fio_u128_or64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_or8`
```c
void fio_u128_or8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add16`
```c
uint16_t fio_u128_reduce_add16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add32`
```c
uint32_t fio_u128_reduce_add32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add64`
```c
uint64_t fio_u128_reduce_add64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_add8`
```c
uint8_t fio_u128_reduce_add8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and16`
```c
uint16_t fio_u128_reduce_and16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and32`
```c
uint32_t fio_u128_reduce_and32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and64`
```c
uint64_t fio_u128_reduce_and64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_and8`
```c
uint8_t fio_u128_reduce_and8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul16`
```c
uint16_t fio_u128_reduce_mul16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul32`
```c
uint32_t fio_u128_reduce_mul32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul64`
```c
uint64_t fio_u128_reduce_mul64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_mul8`
```c
uint8_t fio_u128_reduce_mul8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or16`
```c
uint16_t fio_u128_reduce_or16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or32`
```c
uint32_t fio_u128_reduce_or32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or64`
```c
uint64_t fio_u128_reduce_or64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_or8`
```c
uint8_t fio_u128_reduce_or8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub16`
```c
uint16_t fio_u128_reduce_sub16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub32`
```c
uint32_t fio_u128_reduce_sub32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub64`
```c
uint64_t fio_u128_reduce_sub64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_sub8`
```c
uint8_t fio_u128_reduce_sub8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor16`
```c
uint16_t fio_u128_reduce_xor16(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor32`
```c
uint32_t fio_u128_reduce_xor32(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor64`
```c
uint64_t fio_u128_reduce_xor64(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_reduce_xor8`
```c
uint8_t fio_u128_reduce_xor8(const fio_u128 *a)
```
_Symbol type:_ `function`
#### `fio_u128_rrot16`
```c
void fio_u128_rrot16(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot32`
```c
void fio_u128_rrot32(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot64`
```c
void fio_u128_rrot64(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_rrot8`
```c
void fio_u128_rrot8(fio_u128 *target, const fio_u128 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u128_sub16`
```c
void fio_u128_sub16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub32`
```c
void fio_u128_sub32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub64`
```c
void fio_u128_sub64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_sub8`
```c
void fio_u128_sub8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor`
```c
void fio_u128_xor(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor16`
```c
void fio_u128_xor16(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor32`
```c
void fio_u128_xor32(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor64`
```c
void fio_u128_xor64(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u128_xor8`
```c
void fio_u128_xor8(fio_u128 *target, const fio_u128 *a, const fio_u128 *b)
```
_Symbol type:_ `function`
#### `fio_u256_3xor`
```c
void fio_u256_3xor(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_3xor32`
```c
void fio_u256_3xor32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_3xor64`
```c
void fio_u256_3xor64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_add16`
```c
void fio_u256_add16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add32`
```c
void fio_u256_add32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add64`
```c
void fio_u256_add64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_add8`
```c
void fio_u256_add8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and`
```c
void fio_u256_and(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and16`
```c
void fio_u256_and16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and32`
```c
void fio_u256_and32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and64`
```c
void fio_u256_and64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_and8`
```c
void fio_u256_and8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd16`
```c
void fio_u256_cadd16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd32`
```c
void fio_u256_cadd32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd64`
```c
void fio_u256_cadd64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cadd8`
```c
void fio_u256_cadd8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand16`
```c
void fio_u256_cand16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand32`
```c
void fio_u256_cand32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand64`
```c
void fio_u256_cand64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cand8`
```c
void fio_u256_cand8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_clrot16`
```c
void fio_u256_clrot16(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot32`
```c
void fio_u256_clrot32(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot64`
```c
void fio_u256_clrot64(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_clrot8`
```c
void fio_u256_clrot8(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_cmul16`
```c
void fio_u256_cmul16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul32`
```c
void fio_u256_cmul32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul64`
```c
void fio_u256_cmul64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cmul8`
```c
void fio_u256_cmul8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor16`
```c
void fio_u256_cor16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor32`
```c
void fio_u256_cor32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor64`
```c
void fio_u256_cor64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cor8`
```c
void fio_u256_cor8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_crrot16`
```c
void fio_u256_crrot16(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot32`
```c
void fio_u256_crrot32(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot64`
```c
void fio_u256_crrot64(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_crrot8`
```c
void fio_u256_crrot8(fio_u256 *target, const fio_u256 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u256_csub16`
```c
void fio_u256_csub16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub32`
```c
void fio_u256_csub32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub64`
```c
void fio_u256_csub64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_csub8`
```c
void fio_u256_csub8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_ct_swap_if`
```c
void fio_u256_ct_swap_if(bool cond, fio_u256 *restrict a, fio_u256 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor16`
```c
void fio_u256_cxor16(fio_u256 *target, const fio_u256 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor32`
```c
void fio_u256_cxor32(fio_u256 *target, const fio_u256 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor64`
```c
void fio_u256_cxor64(fio_u256 *target, const fio_u256 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u256_cxor8`
```c
void fio_u256_cxor8(fio_u256 *target, const fio_u256 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u256_inv`
```c
void fio_u256_inv(fio_u256 *target, const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_is_eq`
```c
bool fio_u256_is_eq(const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_lrot16`
```c
void fio_u256_lrot16(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot32`
```c
void fio_u256_lrot32(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot64`
```c
void fio_u256_lrot64(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_lrot8`
```c
void fio_u256_lrot8(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_maj`
```c
void fio_u256_maj(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_maj32`
```c
void fio_u256_maj32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_maj64`
```c
void fio_u256_maj64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mul16`
```c
void fio_u256_mul16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul32`
```c
void fio_u256_mul32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul64`
```c
void fio_u256_mul64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mul8`
```c
void fio_u256_mul8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_mux`
```c
void fio_u256_mux(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mux32`
```c
void fio_u256_mux32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_mux64`
```c
void fio_u256_mux64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b, const fio_u256 *c)
```
_Symbol type:_ `function`
#### `fio_u256_or`
```c
void fio_u256_or(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or16`
```c
void fio_u256_or16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or32`
```c
void fio_u256_or32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or64`
```c
void fio_u256_or64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_or8`
```c
void fio_u256_or8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add16`
```c
uint16_t fio_u256_reduce_add16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add32`
```c
uint32_t fio_u256_reduce_add32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add64`
```c
uint64_t fio_u256_reduce_add64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_add8`
```c
uint8_t fio_u256_reduce_add8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and16`
```c
uint16_t fio_u256_reduce_and16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and32`
```c
uint32_t fio_u256_reduce_and32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and64`
```c
uint64_t fio_u256_reduce_and64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_and8`
```c
uint8_t fio_u256_reduce_and8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul16`
```c
uint16_t fio_u256_reduce_mul16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul32`
```c
uint32_t fio_u256_reduce_mul32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul64`
```c
uint64_t fio_u256_reduce_mul64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_mul8`
```c
uint8_t fio_u256_reduce_mul8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or16`
```c
uint16_t fio_u256_reduce_or16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or32`
```c
uint32_t fio_u256_reduce_or32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or64`
```c
uint64_t fio_u256_reduce_or64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_or8`
```c
uint8_t fio_u256_reduce_or8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub16`
```c
uint16_t fio_u256_reduce_sub16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub32`
```c
uint32_t fio_u256_reduce_sub32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub64`
```c
uint64_t fio_u256_reduce_sub64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_sub8`
```c
uint8_t fio_u256_reduce_sub8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor16`
```c
uint16_t fio_u256_reduce_xor16(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor32`
```c
uint32_t fio_u256_reduce_xor32(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor64`
```c
uint64_t fio_u256_reduce_xor64(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_reduce_xor8`
```c
uint8_t fio_u256_reduce_xor8(const fio_u256 *a)
```
_Symbol type:_ `function`
#### `fio_u256_rrot16`
```c
void fio_u256_rrot16(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot32`
```c
void fio_u256_rrot32(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot64`
```c
void fio_u256_rrot64(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_rrot8`
```c
void fio_u256_rrot8(fio_u256 *target, const fio_u256 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u256_sub16`
```c
void fio_u256_sub16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub32`
```c
void fio_u256_sub32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub64`
```c
void fio_u256_sub64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_sub8`
```c
void fio_u256_sub8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor`
```c
void fio_u256_xor(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor16`
```c
void fio_u256_xor16(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor32`
```c
void fio_u256_xor32(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor64`
```c
void fio_u256_xor64(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u256_xor8`
```c
void fio_u256_xor8(fio_u256 *target, const fio_u256 *a, const fio_u256 *b)
```
_Symbol type:_ `function`
#### `fio_u512_3xor`
```c
void fio_u512_3xor(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_3xor32`
```c
void fio_u512_3xor32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_3xor64`
```c
void fio_u512_3xor64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_add16`
```c
void fio_u512_add16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add32`
```c
void fio_u512_add32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add64`
```c
void fio_u512_add64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_add8`
```c
void fio_u512_add8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and`
```c
void fio_u512_and(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and16`
```c
void fio_u512_and16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and32`
```c
void fio_u512_and32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and64`
```c
void fio_u512_and64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_and8`
```c
void fio_u512_and8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd16`
```c
void fio_u512_cadd16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd32`
```c
void fio_u512_cadd32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd64`
```c
void fio_u512_cadd64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cadd8`
```c
void fio_u512_cadd8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand16`
```c
void fio_u512_cand16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand32`
```c
void fio_u512_cand32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand64`
```c
void fio_u512_cand64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cand8`
```c
void fio_u512_cand8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_clrot16`
```c
void fio_u512_clrot16(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot32`
```c
void fio_u512_clrot32(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot64`
```c
void fio_u512_clrot64(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_clrot8`
```c
void fio_u512_clrot8(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_cmul16`
```c
void fio_u512_cmul16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul32`
```c
void fio_u512_cmul32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul64`
```c
void fio_u512_cmul64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cmul8`
```c
void fio_u512_cmul8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor16`
```c
void fio_u512_cor16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor32`
```c
void fio_u512_cor32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor64`
```c
void fio_u512_cor64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cor8`
```c
void fio_u512_cor8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_crrot16`
```c
void fio_u512_crrot16(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot32`
```c
void fio_u512_crrot32(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot64`
```c
void fio_u512_crrot64(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_crrot8`
```c
void fio_u512_crrot8(fio_u512 *target, const fio_u512 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u512_csub16`
```c
void fio_u512_csub16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub32`
```c
void fio_u512_csub32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub64`
```c
void fio_u512_csub64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_csub8`
```c
void fio_u512_csub8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_ct_swap_if`
```c
void fio_u512_ct_swap_if(bool cond, fio_u512 *restrict a, fio_u512 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor16`
```c
void fio_u512_cxor16(fio_u512 *target, const fio_u512 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor32`
```c
void fio_u512_cxor32(fio_u512 *target, const fio_u512 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor64`
```c
void fio_u512_cxor64(fio_u512 *target, const fio_u512 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u512_cxor8`
```c
void fio_u512_cxor8(fio_u512 *target, const fio_u512 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u512_inv`
```c
void fio_u512_inv(fio_u512 *target, const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_is_eq`
```c
bool fio_u512_is_eq(const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_lrot16`
```c
void fio_u512_lrot16(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot32`
```c
void fio_u512_lrot32(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot64`
```c
void fio_u512_lrot64(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_lrot8`
```c
void fio_u512_lrot8(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_maj`
```c
void fio_u512_maj(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_maj32`
```c
void fio_u512_maj32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_maj64`
```c
void fio_u512_maj64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mul16`
```c
void fio_u512_mul16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul32`
```c
void fio_u512_mul32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul64`
```c
void fio_u512_mul64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mul8`
```c
void fio_u512_mul8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_mux`
```c
void fio_u512_mux(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mux32`
```c
void fio_u512_mux32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_mux64`
```c
void fio_u512_mux64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b, const fio_u512 *c)
```
_Symbol type:_ `function`
#### `fio_u512_or`
```c
void fio_u512_or(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or16`
```c
void fio_u512_or16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or32`
```c
void fio_u512_or32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or64`
```c
void fio_u512_or64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_or8`
```c
void fio_u512_or8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add16`
```c
uint16_t fio_u512_reduce_add16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add32`
```c
uint32_t fio_u512_reduce_add32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add64`
```c
uint64_t fio_u512_reduce_add64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_add8`
```c
uint8_t fio_u512_reduce_add8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and16`
```c
uint16_t fio_u512_reduce_and16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and32`
```c
uint32_t fio_u512_reduce_and32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and64`
```c
uint64_t fio_u512_reduce_and64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_and8`
```c
uint8_t fio_u512_reduce_and8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul16`
```c
uint16_t fio_u512_reduce_mul16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul32`
```c
uint32_t fio_u512_reduce_mul32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul64`
```c
uint64_t fio_u512_reduce_mul64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_mul8`
```c
uint8_t fio_u512_reduce_mul8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or16`
```c
uint16_t fio_u512_reduce_or16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or32`
```c
uint32_t fio_u512_reduce_or32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or64`
```c
uint64_t fio_u512_reduce_or64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_or8`
```c
uint8_t fio_u512_reduce_or8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub16`
```c
uint16_t fio_u512_reduce_sub16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub32`
```c
uint32_t fio_u512_reduce_sub32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub64`
```c
uint64_t fio_u512_reduce_sub64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_sub8`
```c
uint8_t fio_u512_reduce_sub8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor16`
```c
uint16_t fio_u512_reduce_xor16(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor32`
```c
uint32_t fio_u512_reduce_xor32(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor64`
```c
uint64_t fio_u512_reduce_xor64(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_reduce_xor8`
```c
uint8_t fio_u512_reduce_xor8(const fio_u512 *a)
```
_Symbol type:_ `function`
#### `fio_u512_rrot16`
```c
void fio_u512_rrot16(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot32`
```c
void fio_u512_rrot32(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot64`
```c
void fio_u512_rrot64(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_rrot8`
```c
void fio_u512_rrot8(fio_u512 *target, const fio_u512 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u512_sub16`
```c
void fio_u512_sub16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub32`
```c
void fio_u512_sub32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub64`
```c
void fio_u512_sub64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_sub8`
```c
void fio_u512_sub8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor`
```c
void fio_u512_xor(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor16`
```c
void fio_u512_xor16(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor32`
```c
void fio_u512_xor32(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor64`
```c
void fio_u512_xor64(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u512_xor8`
```c
void fio_u512_xor8(fio_u512 *target, const fio_u512 *a, const fio_u512 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor`
```c
void fio_u1024_3xor(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor32`
```c
void fio_u1024_3xor32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_3xor64`
```c
void fio_u1024_3xor64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_add16`
```c
void fio_u1024_add16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add32`
```c
void fio_u1024_add32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add64`
```c
void fio_u1024_add64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_add8`
```c
void fio_u1024_add8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and`
```c
void fio_u1024_and(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and16`
```c
void fio_u1024_and16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and32`
```c
void fio_u1024_and32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and64`
```c
void fio_u1024_and64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_and8`
```c
void fio_u1024_and8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd16`
```c
void fio_u1024_cadd16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd32`
```c
void fio_u1024_cadd32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd64`
```c
void fio_u1024_cadd64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cadd8`
```c
void fio_u1024_cadd8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand16`
```c
void fio_u1024_cand16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand32`
```c
void fio_u1024_cand32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand64`
```c
void fio_u1024_cand64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cand8`
```c
void fio_u1024_cand8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot16`
```c
void fio_u1024_clrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot32`
```c
void fio_u1024_clrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot64`
```c
void fio_u1024_clrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_clrot8`
```c
void fio_u1024_clrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul16`
```c
void fio_u1024_cmul16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul32`
```c
void fio_u1024_cmul32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul64`
```c
void fio_u1024_cmul64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cmul8`
```c
void fio_u1024_cmul8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor16`
```c
void fio_u1024_cor16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor32`
```c
void fio_u1024_cor32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor64`
```c
void fio_u1024_cor64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cor8`
```c
void fio_u1024_cor8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot16`
```c
void fio_u1024_crrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot32`
```c
void fio_u1024_crrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot64`
```c
void fio_u1024_crrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_crrot8`
```c
void fio_u1024_crrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u1024_csub16`
```c
void fio_u1024_csub16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub32`
```c
void fio_u1024_csub32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub64`
```c
void fio_u1024_csub64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_csub8`
```c
void fio_u1024_csub8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_ct_swap_if`
```c
void fio_u1024_ct_swap_if(bool cond, fio_u1024 *restrict a, fio_u1024 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor16`
```c
void fio_u1024_cxor16(fio_u1024 *target, const fio_u1024 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor32`
```c
void fio_u1024_cxor32(fio_u1024 *target, const fio_u1024 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor64`
```c
void fio_u1024_cxor64(fio_u1024 *target, const fio_u1024 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_cxor8`
```c
void fio_u1024_cxor8(fio_u1024 *target, const fio_u1024 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u1024_inv`
```c
void fio_u1024_inv(fio_u1024 *target, const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_is_eq`
```c
bool fio_u1024_is_eq(const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot16`
```c
void fio_u1024_lrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot32`
```c
void fio_u1024_lrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot64`
```c
void fio_u1024_lrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_lrot8`
```c
void fio_u1024_lrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_maj`
```c
void fio_u1024_maj(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_maj32`
```c
void fio_u1024_maj32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_maj64`
```c
void fio_u1024_maj64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mul16`
```c
void fio_u1024_mul16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul32`
```c
void fio_u1024_mul32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul64`
```c
void fio_u1024_mul64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mul8`
```c
void fio_u1024_mul8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_mux`
```c
void fio_u1024_mux(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mux32`
```c
void fio_u1024_mux32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_mux64`
```c
void fio_u1024_mux64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *c)
```
_Symbol type:_ `function`
#### `fio_u1024_or`
```c
void fio_u1024_or(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or16`
```c
void fio_u1024_or16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or32`
```c
void fio_u1024_or32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or64`
```c
void fio_u1024_or64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_or8`
```c
void fio_u1024_or8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add16`
```c
uint16_t fio_u1024_reduce_add16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add32`
```c
uint32_t fio_u1024_reduce_add32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add64`
```c
uint64_t fio_u1024_reduce_add64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_add8`
```c
uint8_t fio_u1024_reduce_add8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and16`
```c
uint16_t fio_u1024_reduce_and16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and32`
```c
uint32_t fio_u1024_reduce_and32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and64`
```c
uint64_t fio_u1024_reduce_and64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_and8`
```c
uint8_t fio_u1024_reduce_and8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul16`
```c
uint16_t fio_u1024_reduce_mul16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul32`
```c
uint32_t fio_u1024_reduce_mul32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul64`
```c
uint64_t fio_u1024_reduce_mul64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_mul8`
```c
uint8_t fio_u1024_reduce_mul8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or16`
```c
uint16_t fio_u1024_reduce_or16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or32`
```c
uint32_t fio_u1024_reduce_or32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or64`
```c
uint64_t fio_u1024_reduce_or64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_or8`
```c
uint8_t fio_u1024_reduce_or8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub16`
```c
uint16_t fio_u1024_reduce_sub16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub32`
```c
uint32_t fio_u1024_reduce_sub32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub64`
```c
uint64_t fio_u1024_reduce_sub64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_sub8`
```c
uint8_t fio_u1024_reduce_sub8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor16`
```c
uint16_t fio_u1024_reduce_xor16(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor32`
```c
uint32_t fio_u1024_reduce_xor32(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor64`
```c
uint64_t fio_u1024_reduce_xor64(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_reduce_xor8`
```c
uint8_t fio_u1024_reduce_xor8(const fio_u1024 *a)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot16`
```c
void fio_u1024_rrot16(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot32`
```c
void fio_u1024_rrot32(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot64`
```c
void fio_u1024_rrot64(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_rrot8`
```c
void fio_u1024_rrot8(fio_u1024 *target, const fio_u1024 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u1024_sub16`
```c
void fio_u1024_sub16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub32`
```c
void fio_u1024_sub32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub64`
```c
void fio_u1024_sub64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_sub8`
```c
void fio_u1024_sub8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor`
```c
void fio_u1024_xor(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor16`
```c
void fio_u1024_xor16(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor32`
```c
void fio_u1024_xor32(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor64`
```c
void fio_u1024_xor64(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u1024_xor8`
```c
void fio_u1024_xor8(fio_u1024 *target, const fio_u1024 *a, const fio_u1024 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor`
```c
void fio_u2048_3xor(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor32`
```c
void fio_u2048_3xor32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_3xor64`
```c
void fio_u2048_3xor64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_add16`
```c
void fio_u2048_add16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add32`
```c
void fio_u2048_add32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add64`
```c
void fio_u2048_add64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_add8`
```c
void fio_u2048_add8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and`
```c
void fio_u2048_and(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and16`
```c
void fio_u2048_and16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and32`
```c
void fio_u2048_and32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and64`
```c
void fio_u2048_and64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_and8`
```c
void fio_u2048_and8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd16`
```c
void fio_u2048_cadd16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd32`
```c
void fio_u2048_cadd32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd64`
```c
void fio_u2048_cadd64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cadd8`
```c
void fio_u2048_cadd8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand16`
```c
void fio_u2048_cand16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand32`
```c
void fio_u2048_cand32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand64`
```c
void fio_u2048_cand64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cand8`
```c
void fio_u2048_cand8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot16`
```c
void fio_u2048_clrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot32`
```c
void fio_u2048_clrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot64`
```c
void fio_u2048_clrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_clrot8`
```c
void fio_u2048_clrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul16`
```c
void fio_u2048_cmul16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul32`
```c
void fio_u2048_cmul32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul64`
```c
void fio_u2048_cmul64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cmul8`
```c
void fio_u2048_cmul8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor16`
```c
void fio_u2048_cor16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor32`
```c
void fio_u2048_cor32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor64`
```c
void fio_u2048_cor64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cor8`
```c
void fio_u2048_cor8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot16`
```c
void fio_u2048_crrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot32`
```c
void fio_u2048_crrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot64`
```c
void fio_u2048_crrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_crrot8`
```c
void fio_u2048_crrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u2048_csub16`
```c
void fio_u2048_csub16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub32`
```c
void fio_u2048_csub32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub64`
```c
void fio_u2048_csub64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_csub8`
```c
void fio_u2048_csub8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_ct_swap_if`
```c
void fio_u2048_ct_swap_if(bool cond, fio_u2048 *restrict a, fio_u2048 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor16`
```c
void fio_u2048_cxor16(fio_u2048 *target, const fio_u2048 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor32`
```c
void fio_u2048_cxor32(fio_u2048 *target, const fio_u2048 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor64`
```c
void fio_u2048_cxor64(fio_u2048 *target, const fio_u2048 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_cxor8`
```c
void fio_u2048_cxor8(fio_u2048 *target, const fio_u2048 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u2048_inv`
```c
void fio_u2048_inv(fio_u2048 *target, const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_is_eq`
```c
bool fio_u2048_is_eq(const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot16`
```c
void fio_u2048_lrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot32`
```c
void fio_u2048_lrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot64`
```c
void fio_u2048_lrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_lrot8`
```c
void fio_u2048_lrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_maj`
```c
void fio_u2048_maj(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_maj32`
```c
void fio_u2048_maj32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_maj64`
```c
void fio_u2048_maj64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mul16`
```c
void fio_u2048_mul16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul32`
```c
void fio_u2048_mul32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul64`
```c
void fio_u2048_mul64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mul8`
```c
void fio_u2048_mul8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_mux`
```c
void fio_u2048_mux(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mux32`
```c
void fio_u2048_mux32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_mux64`
```c
void fio_u2048_mux64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *c)
```
_Symbol type:_ `function`
#### `fio_u2048_or`
```c
void fio_u2048_or(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or16`
```c
void fio_u2048_or16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or32`
```c
void fio_u2048_or32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or64`
```c
void fio_u2048_or64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_or8`
```c
void fio_u2048_or8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add16`
```c
uint16_t fio_u2048_reduce_add16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add32`
```c
uint32_t fio_u2048_reduce_add32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add64`
```c
uint64_t fio_u2048_reduce_add64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_add8`
```c
uint8_t fio_u2048_reduce_add8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and16`
```c
uint16_t fio_u2048_reduce_and16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and32`
```c
uint32_t fio_u2048_reduce_and32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and64`
```c
uint64_t fio_u2048_reduce_and64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_and8`
```c
uint8_t fio_u2048_reduce_and8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul16`
```c
uint16_t fio_u2048_reduce_mul16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul32`
```c
uint32_t fio_u2048_reduce_mul32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul64`
```c
uint64_t fio_u2048_reduce_mul64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_mul8`
```c
uint8_t fio_u2048_reduce_mul8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or16`
```c
uint16_t fio_u2048_reduce_or16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or32`
```c
uint32_t fio_u2048_reduce_or32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or64`
```c
uint64_t fio_u2048_reduce_or64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_or8`
```c
uint8_t fio_u2048_reduce_or8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub16`
```c
uint16_t fio_u2048_reduce_sub16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub32`
```c
uint32_t fio_u2048_reduce_sub32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub64`
```c
uint64_t fio_u2048_reduce_sub64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_sub8`
```c
uint8_t fio_u2048_reduce_sub8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor16`
```c
uint16_t fio_u2048_reduce_xor16(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor32`
```c
uint32_t fio_u2048_reduce_xor32(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor64`
```c
uint64_t fio_u2048_reduce_xor64(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_reduce_xor8`
```c
uint8_t fio_u2048_reduce_xor8(const fio_u2048 *a)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot16`
```c
void fio_u2048_rrot16(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot32`
```c
void fio_u2048_rrot32(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot64`
```c
void fio_u2048_rrot64(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_rrot8`
```c
void fio_u2048_rrot8(fio_u2048 *target, const fio_u2048 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u2048_sub16`
```c
void fio_u2048_sub16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub32`
```c
void fio_u2048_sub32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub64`
```c
void fio_u2048_sub64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_sub8`
```c
void fio_u2048_sub8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor`
```c
void fio_u2048_xor(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor16`
```c
void fio_u2048_xor16(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor32`
```c
void fio_u2048_xor32(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor64`
```c
void fio_u2048_xor64(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u2048_xor8`
```c
void fio_u2048_xor8(fio_u2048 *target, const fio_u2048 *a, const fio_u2048 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor`
```c
void fio_u4096_3xor(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor32`
```c
void fio_u4096_3xor32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_3xor64`
```c
void fio_u4096_3xor64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_add16`
```c
void fio_u4096_add16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add32`
```c
void fio_u4096_add32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add64`
```c
void fio_u4096_add64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_add8`
```c
void fio_u4096_add8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and`
```c
void fio_u4096_and(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and16`
```c
void fio_u4096_and16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and32`
```c
void fio_u4096_and32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and64`
```c
void fio_u4096_and64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_and8`
```c
void fio_u4096_and8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd16`
```c
void fio_u4096_cadd16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd32`
```c
void fio_u4096_cadd32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd64`
```c
void fio_u4096_cadd64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cadd8`
```c
void fio_u4096_cadd8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand16`
```c
void fio_u4096_cand16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand32`
```c
void fio_u4096_cand32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand64`
```c
void fio_u4096_cand64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cand8`
```c
void fio_u4096_cand8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot16`
```c
void fio_u4096_clrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot32`
```c
void fio_u4096_clrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot64`
```c
void fio_u4096_clrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_clrot8`
```c
void fio_u4096_clrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul16`
```c
void fio_u4096_cmul16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul32`
```c
void fio_u4096_cmul32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul64`
```c
void fio_u4096_cmul64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cmul8`
```c
void fio_u4096_cmul8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor16`
```c
void fio_u4096_cor16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor32`
```c
void fio_u4096_cor32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor64`
```c
void fio_u4096_cor64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cor8`
```c
void fio_u4096_cor8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot16`
```c
void fio_u4096_crrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot32`
```c
void fio_u4096_crrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot64`
```c
void fio_u4096_crrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_crrot8`
```c
void fio_u4096_crrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t bts)
```
_Symbol type:_ `function`
#### `fio_u4096_csub16`
```c
void fio_u4096_csub16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub32`
```c
void fio_u4096_csub32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub64`
```c
void fio_u4096_csub64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_csub8`
```c
void fio_u4096_csub8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_ct_swap_if`
```c
void fio_u4096_ct_swap_if(bool cond, fio_u4096 *restrict a, fio_u4096 *restrict b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor16`
```c
void fio_u4096_cxor16(fio_u4096 *target, const fio_u4096 *a, uint16_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor32`
```c
void fio_u4096_cxor32(fio_u4096 *target, const fio_u4096 *a, uint32_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor64`
```c
void fio_u4096_cxor64(fio_u4096 *target, const fio_u4096 *a, uint64_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_cxor8`
```c
void fio_u4096_cxor8(fio_u4096 *target, const fio_u4096 *a, uint8_t b)
```
_Symbol type:_ `function`
#### `fio_u4096_inv`
```c
void fio_u4096_inv(fio_u4096 *target, const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_is_eq`
```c
bool fio_u4096_is_eq(const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot16`
```c
void fio_u4096_lrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot32`
```c
void fio_u4096_lrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot64`
```c
void fio_u4096_lrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_lrot8`
```c
void fio_u4096_lrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_maj`
```c
void fio_u4096_maj(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_maj32`
```c
void fio_u4096_maj32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_maj64`
```c
void fio_u4096_maj64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mul16`
```c
void fio_u4096_mul16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul32`
```c
void fio_u4096_mul32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul64`
```c
void fio_u4096_mul64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mul8`
```c
void fio_u4096_mul8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_mux`
```c
void fio_u4096_mux(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mux32`
```c
void fio_u4096_mux32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_mux64`
```c
void fio_u4096_mux64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b, const fio_u4096 *c)
```
_Symbol type:_ `function`
#### `fio_u4096_or`
```c
void fio_u4096_or(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or16`
```c
void fio_u4096_or16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or32`
```c
void fio_u4096_or32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or64`
```c
void fio_u4096_or64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_or8`
```c
void fio_u4096_or8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add16`
```c
uint16_t fio_u4096_reduce_add16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add32`
```c
uint32_t fio_u4096_reduce_add32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add64`
```c
uint64_t fio_u4096_reduce_add64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_add8`
```c
uint8_t fio_u4096_reduce_add8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and16`
```c
uint16_t fio_u4096_reduce_and16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and32`
```c
uint32_t fio_u4096_reduce_and32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and64`
```c
uint64_t fio_u4096_reduce_and64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_and8`
```c
uint8_t fio_u4096_reduce_and8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul16`
```c
uint16_t fio_u4096_reduce_mul16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul32`
```c
uint32_t fio_u4096_reduce_mul32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul64`
```c
uint64_t fio_u4096_reduce_mul64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_mul8`
```c
uint8_t fio_u4096_reduce_mul8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or16`
```c
uint16_t fio_u4096_reduce_or16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or32`
```c
uint32_t fio_u4096_reduce_or32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or64`
```c
uint64_t fio_u4096_reduce_or64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_or8`
```c
uint8_t fio_u4096_reduce_or8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub16`
```c
uint16_t fio_u4096_reduce_sub16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub32`
```c
uint32_t fio_u4096_reduce_sub32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub64`
```c
uint64_t fio_u4096_reduce_sub64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_sub8`
```c
uint8_t fio_u4096_reduce_sub8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor16`
```c
uint16_t fio_u4096_reduce_xor16(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor32`
```c
uint32_t fio_u4096_reduce_xor32(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor64`
```c
uint64_t fio_u4096_reduce_xor64(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_reduce_xor8`
```c
uint8_t fio_u4096_reduce_xor8(const fio_u4096 *a)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot16`
```c
void fio_u4096_rrot16(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot32`
```c
void fio_u4096_rrot32(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot64`
```c
void fio_u4096_rrot64(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_rrot8`
```c
void fio_u4096_rrot8(fio_u4096 *target, const fio_u4096 *a, const uint8_t *rotations)
```
_Symbol type:_ `function`
#### `fio_u4096_sub16`
```c
void fio_u4096_sub16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub32`
```c
void fio_u4096_sub32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub64`
```c
void fio_u4096_sub64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_sub8`
```c
void fio_u4096_sub8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor`
```c
void fio_u4096_xor(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor16`
```c
void fio_u4096_xor16(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor32`
```c
void fio_u4096_xor32(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor64`
```c
void fio_u4096_xor64(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u4096_xor8`
```c
void fio_u4096_xor8(fio_u4096 *target, const fio_u4096 *a, const fio_u4096 *b)
```
_Symbol type:_ `function`
#### `fio_u128_addv64`
```c
fio_u128 fio_u128_addv64(fio_u128 a, fio_u128 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_andv`
```c
fio_u128 fio_u128_andv(fio_u128 a, fio_u128 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_orv`
```c
fio_u128 fio_u128_orv(fio_u128 a, fio_u128 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_xorv`
```c
fio_u128 fio_u128_xorv(fio_u128 a, fio_u128 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_addv64`
```c
fio_u256 fio_u256_addv64(fio_u256 a, fio_u256 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_andv`
```c
fio_u256 fio_u256_andv(fio_u256 a, fio_u256 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_orv`
```c
fio_u256 fio_u256_orv(fio_u256 a, fio_u256 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u256_xorv`
```c
fio_u256 fio_u256_xorv(fio_u256 a, fio_u256 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_addv64`
```c
fio_u512 fio_u512_addv64(fio_u512 a, fio_u512 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_andv`
```c
fio_u512 fio_u512_andv(fio_u512 a, fio_u512 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_orv`
```c
fio_u512 fio_u512_orv(fio_u512 a, fio_u512 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u512_xorv`
```c
fio_u512 fio_u512_xorv(fio_u512 a, fio_u512 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_addv64`
```c
fio_u1024 fio_u1024_addv64(fio_u1024 a, fio_u1024 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_andv`
```c
fio_u1024 fio_u1024_andv(fio_u1024 a, fio_u1024 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_orv`
```c
fio_u1024 fio_u1024_orv(fio_u1024 a, fio_u1024 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u1024_xorv`
```c
fio_u1024 fio_u1024_xorv(fio_u1024 a, fio_u1024 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_addv64`
```c
fio_u2048 fio_u2048_addv64(fio_u2048 a, fio_u2048 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_andv`
```c
fio_u2048 fio_u2048_andv(fio_u2048 a, fio_u2048 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_orv`
```c
fio_u2048 fio_u2048_orv(fio_u2048 a, fio_u2048 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u2048_xorv`
```c
fio_u2048 fio_u2048_xorv(fio_u2048 a, fio_u2048 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_addv64`
```c
fio_u4096 fio_u4096_addv64(fio_u4096 a, fio_u4096 b)
```
ADD two values as 64-bit lanes, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_andv`
```c
fio_u4096 fio_u4096_andv(fio_u4096 a, fio_u4096 b)
```
AND two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_orv`
```c
fio_u4096 fio_u4096_orv(fio_u4096 a, fio_u4096 b)
```
OR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u4096_xorv`
```c
fio_u4096 fio_u4096_xorv(fio_u4096 a, fio_u4096 b)
```
XOR two values, returning result by value.
_Symbol type:_ `function`
#### `fio_u128_add`
```c
uint64_t fio_u128_add(fio_u128 *result, const fio_u128 *a, const fio_u128 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u128_cmp`
```c
int fio_u128_cmp(const fio_u128 *a, const fio_u128 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u128_sub`
```c
uint64_t fio_u128_sub(fio_u128 *result, const fio_u128 *a, const fio_u128 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u256_add`
```c
uint64_t fio_u256_add(fio_u256 *result, const fio_u256 *a, const fio_u256 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u256_cmp`
```c
int fio_u256_cmp(const fio_u256 *a, const fio_u256 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u256_sub`
```c
uint64_t fio_u256_sub(fio_u256 *result, const fio_u256 *a, const fio_u256 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u512_add`
```c
uint64_t fio_u512_add(fio_u512 *result, const fio_u512 *a, const fio_u512 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u512_cmp`
```c
int fio_u512_cmp(const fio_u512 *a, const fio_u512 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u512_sub`
```c
uint64_t fio_u512_sub(fio_u512 *result, const fio_u512 *a, const fio_u512 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u1024_add`
```c
uint64_t fio_u1024_add(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u1024_cmp`
```c
int fio_u1024_cmp(const fio_u1024 *a, const fio_u1024 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u1024_sub`
```c
uint64_t fio_u1024_sub(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u2048_add`
```c
uint64_t fio_u2048_add(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u2048_cmp`
```c
int fio_u2048_cmp(const fio_u2048 *a, const fio_u2048 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u2048_sub`
```c
uint64_t fio_u2048_sub(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u4096_add`
```c
uint64_t fio_u4096_add(fio_u4096 *result, const fio_u4096 *a, const fio_u4096 *b)
```
Performs A+B, storing in `result`. Return the carry bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u4096_cmp`
```c
int fio_u4096_cmp(const fio_u4096 *a, const fio_u4096 *b)
```
Returns -1, 0, or 1 if a < b, a == b or a > a (respectively).
_Symbol type:_ `function`
#### `fio_u4096_sub`
```c
uint64_t fio_u4096_sub(fio_u4096 *result, const fio_u4096 *a, const fio_u4096 *b)
```
Performs A-B, storing in `result`. Returns the borrow bit (1 or 0).
_Symbol type:_ `function`
#### `fio_u128_montgomery_mul`
```c
void fio_u128_montgomery_mul(fio_u128 *result, const fio_u128 *a, const fio_u128 *b, const fio_u128 *N, const fio_u128 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u128_mul`
```c
void fio_u128_mul(fio_u256 *result, const fio_u128 *a, const fio_u128 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u256_montgomery_mul`
```c
void fio_u256_montgomery_mul(fio_u256 *result, const fio_u256 *a, const fio_u256 *b, const fio_u256 *N, const fio_u256 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u256_mul`
```c
void fio_u256_mul(fio_u512 *result, const fio_u256 *a, const fio_u256 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u512_montgomery_mul`
```c
void fio_u512_montgomery_mul(fio_u512 *result, const fio_u512 *a, const fio_u512 *b, const fio_u512 *N, const fio_u512 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u512_mul`
```c
void fio_u512_mul(fio_u1024 *result, const fio_u512 *a, const fio_u512 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u1024_montgomery_mul`
```c
void fio_u1024_montgomery_mul(fio_u1024 *result, const fio_u1024 *a, const fio_u1024 *b, const fio_u1024 *N, const fio_u1024 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u1024_mul`
```c
void fio_u1024_mul(fio_u2048 *result, const fio_u1024 *a, const fio_u1024 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_u2048_montgomery_mul`
```c
void fio_u2048_montgomery_mul(fio_u2048 *result, const fio_u2048 *a, const fio_u2048 *b, const fio_u2048 *N, const fio_u2048 *N_dash)
```
_Symbol type:_ `function`
#### `fio_u2048_mul`
```c
void fio_u2048_mul(fio_u4096 *result, const fio_u2048 *a, const fio_u2048 *b)
```
Multiplies A and B, storing the result in `result`.
_Symbol type:_ `function`
#### `fio_cycle_counter`
```c
inline uint64_t fio_cycle_counter(void)
```
_Symbol type:_ `function`
#### `fio_utf8_code_len`
```c
inline FIO_CONST unsigned fio_utf8_code_len(uint32_t u)
```
_Symbol type:_ `function`
#### `fio_utf8_char_len_unsafe`
```c
inline FIO_CONST unsigned fio_utf8_char_len_unsafe(uint8_t c)
```
Returns 1-4 (UTF-8 char length), 8 (middle of a char) or 0 (invalid).
_Symbol type:_ `function`
#### `fio_utf8_char_len`
```c
inline unsigned fio_utf8_char_len(const void *str_)
```
Returns the number of valid UTF-8 bytes used by first char at `str`.
_Symbol type:_ `function`
#### `fio_utf8_write`
```c
inline unsigned fio_utf8_write(void *dest_, uint32_t u)
```
Writes code point to `dest` using UFT-8. Returns number of bytes written.
_Symbol type:_ `function`
#### `fio_utf8_read`
```c
inline uint32_t fio_utf8_read(char **str)
```
Decodes the first UTF-8 char at `str` and returns its code point value.
Advances the pointer at `str` by the number of bytes consumed (read).
_Symbol type:_ `function`
#### `fio_utf8_peek`
```c
inline uint32_t fio_utf8_peek(const char *str)
```
Decodes the first UTF-8 char at `str` and returns its code point value.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/001 header.h`
12 public symbols.
### Macros
#### `FIO_MEM_REALLOC`
```c
#define FIO_MEM_REALLOC(ptr, old_size, new_size, copy_len) \
fio_realloc2((ptr), (new_size), (copy_len))
```
Reallocates memory, copying (at least) `copy_len` if necessary.
_Symbol type:_ `macro`
#### `FIO_MEM_FREE`
```c
#define FIO_MEM_FREE(ptr, size) fio_free((ptr))
```
Frees allocated memory.
_Symbol type:_ `macro`
#### `FIO_MEM_REALLOC_IS_SAFE`
```c
#define FIO_MEM_REALLOC_IS_SAFE fio_realloc_is_safe()
```
Set to true of internall allocator is used (memory returned set to zero).
_Symbol type:_ `macro`
#### `FIO_MEM_ALIGNMENT_SIZE`
```c
#define FIO_MEM_ALIGNMENT_SIZE fio_malloc_alignment()
```
Detect allocator allignment dynamically.
_Symbol type:_ `macro`
#### `FIO_MEM_REALLOC_ALIGNED`
```c
#define FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment) \
((ptr) = fio_realloc_aligned((ptr), (new_size), (copy_len), (alignment)))
```
Reallocates memory with an alignment requirement, assigning `ptr`.
_Symbol type:_ `macro`
#### `FIO_MEM_ALLOC_SIZE`
```c
#define FIO_MEM_ALLOC_SIZE(size) fio_alloc_size((size))
```
Returns the usable size the allocator reserves for a `size` request.
_Symbol type:_ `macro`
#### `FIO_MEM_FREE_ALIGNED`
```c
#define FIO_MEM_FREE_ALIGNED(ptr, size) fio___aligned_free_fallback((ptr))
```
Frees memory allocated using FIO_MEM_REALLOC_ALIGNED.
_Symbol type:_ `macro`
#### `FIO_MEM_SYS_ALIGN_MAX_LOG`
```c
#define FIO_MEM_SYS_ALIGN_MAX_LOG 21
```
Mirrors the custom allocator's default FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG.
_Symbol type:_ `macro`
#### `FIO_PTR_TAG`
```c
#define FIO_PTR_TAG(p) (p)
```
Supports embedded pointer tagging / untagging for the included types.
Should resolve to a tagged pointer value. i.e.: ((uintptr_t)(p) | 1)
_Symbol type:_ `macro`
#### `FIO_PTR_UNTAG`
```c
#define FIO_PTR_UNTAG(p) (p)
```
Supports embedded pointer tagging / untagging for the included types.
Should resolve to an untagged pointer value. i.e.: ((uintptr_t)(p) | ~1UL)
_Symbol type:_ `macro`
#### `FIO_PTR_TAG_VALIDATE`
```c
#define FIO_PTR_TAG_VALIDATE(ptr) ((ptr) != NULL)
```
If FIO_PTR_TAG_VALIDATE is defined, tagging will be verified before executing
any code.
FIO_PTR_TAG_VALIDATE must fail on NULL pointers.
_Symbol type:_ `macro`
#### `FIO_PTR_TAG_GET_UNTAGGED`
```c
#define FIO_PTR_TAG_GET_UNTAGGED(untagged_type, tagged_ptr) \
((untagged_type *)(FIO_PTR_UNTAG((tagged_ptr))))
```
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/001 logging.h`
1 public symbols.
### Macros
#### `FIO_LOG_LEVEL_DEFAULT`
```c
#define FIO_LOG_LEVEL_DEFAULT FIO_LOG_LEVEL_DEBUG
```
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/001 memalt.h`
5 public symbols.
### Functions
#### `fio_memset`
```c
void *fio_memset(void *restrict dest, uint64_t data, size_t bytes)
```
A somewhat naive implementation of `memset`.
Probably slower than the one included with your compiler's C library.
_Symbol type:_ `function`
#### `fio_memcpy`
```c
void *fio_memcpy(void *dest_, const void *src_, size_t bytes)
```
A somewhat naive implementation of `memcpy`.
Probably slower than the one included with your compiler's C library.
_Symbol type:_ `function`
#### `fio_memchr`
```c
void *fio_memchr(const void *buffer, const char token, size_t len)
```
A token seeking function. This is a fallback for `memchr`, but `memchr`
should be faster.
_Symbol type:_ `function`
#### `fio_memcmp`
```c
int fio_memcmp(const void *a_, const void *b_, size_t len)
```
A comparison function. This is a fallback for `memcmp`, but `memcmp`
should be faster.
_Symbol type:_ `function`
#### `fio_strlen`
```c
size_t fio_strlen(const char *str)
```
An alternative to `strlen` - may raise Address Sanitation errors.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/001 patches.h`
2 public symbols.
### Functions
#### `fio_sys_env`
```c
inline char *fio_sys_env(const char *name)
```
Portable getenv: returns the value of environment variable `name`, or NULL.
Thin wrapper around getenv for API uniformity across platforms.
_Symbol type:_ `function`
#### `fio_sys_env_set`
```c
inline int fio_sys_env_set(const char *name, const char *value, int overwrite)
```
Portable setenv: sets environment variable `name` to `value`.
If `overwrite` is zero and the variable already exists, does nothing.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 atol.h`
49 public symbols.
### Macros
#### `FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER`
```c
#define FIO_ATOL_ALLOW_UNDERSCORE_DIVIDER 1
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_MANT_MASK`
```c
#define FIO_MATH_DBL_MANT_MASK (((uint64_t)1ULL << 52) - 1)
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_EXPO_MASK`
```c
#define FIO_MATH_DBL_EXPO_MASK ((uint64_t)2047ULL << 52)
```
_Symbol type:_ `macro`
#### `FIO_MATH_DBL_SIGN_MASK`
```c
#define FIO_MATH_DBL_SIGN_MASK ((uint64_t)1ULL << 63)
```
_Symbol type:_ `macro`
### Types
#### `fio_aton_s`
```c
typedef struct {
union {
int64_t i;
double f;
uint64_t u;
};
int is_float;
int err;
} fio_aton_s
```
Result type for fio_aton
_Symbol type:_ `type`
### Functions
#### `fio_aton`
```c
FIO_SFUNC fio_aton_s fio_aton(char **pstr)
```
Converts a String to a number - either an integer or a float (double).
Skips white space at the beginning of the string.
Auto detects binary and hex formats when prefix is provided (0x / 0b).
Auto detects octal when number starts with zero.
Auto detects the Strings "inf", "infinity" and "nan" as float values.
The number's format and type are returned in the return type.
If a numerical overflow or format error occurred, the `.err` flag is set.
Note: rounding errors may occur, as this is not an `strtod` exact match.
_Symbol type:_ `function`
#### `fio_atol`
```c
int64_t fio_atol(char **pstr)
```
A helper function that converts between String data to a signed int64_t.
Numbers are assumed to be in base 10. Octal (`0###`), Hex (`0x##`/`x##`) and
binary (`0b##`/ `b##`) are recognized as well. For binary Most Significant
Bit must come first.
The most significant difference between this function and `strtol` (aside of
API design), is the added support for binary representations.
_Symbol type:_ `function`
#### `fio_atof`
```c
double fio_atof(char **pstr)
```
A helper function that converts between String data to a signed double.
_Symbol type:_ `function`
#### `fio_ltoa`
```c
size_t fio_ltoa(char *dest, int64_t num, uint8_t base)
```
A helper function that writes a signed int64_t to a string.
No overflow guard is provided, make sure there's at least 68 bytes available
(for base 2).
Offers special support for base 2 (binary), base 8 (octal), base 10 and base
16 (hex) where prefixes are automatically added if required (i.e.,`"0x"` for
hex and `"0b"` for base 2, and `"0"` for octal).
Supports any base up to base 36 (using 0-9,A-Z).
An unsupported base will log an error and print zero.
Returns the number of bytes actually written (excluding the NUL terminator).
_Symbol type:_ `function`
#### `fio_ftoa`
```c
size_t fio_ftoa(char *dest, double num, uint8_t base)
```
A helper function that converts between a double to a string.
No overflow guard is provided, make sure there's at least 130 bytes
available (for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL
terminator).
_Symbol type:_ `function`
#### `fio_c2i`
```c
uint8_t fio_c2i(unsigned char c)
```
Maps characters to alphanumerical value, where numbers have their natural
values (0-9) and `A-Z` (or `a-z`) are the values 10-35.
Out of bound values return 255.
This allows parsing of numeral strings for up to base 36.
_Symbol type:_ `function`
#### `fio_i2c`
```c
uint8_t fio_i2c(unsigned char i)
```
Maps numeral values to alphanumerical characters, where numbers have their
natural values (0-9) and `A-Z` are the values 10-35.
Accepts values up to 63. Returns zero for values over 35. Out of bound values
produce undefined behavior.
This allows printing of numerals for up to base 36.
_Symbol type:_ `function`
#### `fio_digits10`
```c
inline size_t fio_digits10(int64_t i)
```
Returns the number of digits in base 10.
_Symbol type:_ `function`
#### `fio_digits10u`
```c
FIO_SFUNC size_t fio_digits10u(uint64_t i)
```
Returns the number of digits in base 10 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits8u`
```c
FIO_SFUNC size_t fio_digits8u(uint64_t i)
```
Returns the number of digits in base 8 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits16u`
```c
FIO_SFUNC size_t fio_digits16u(uint64_t i)
```
Returns the number of digits in base 16 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits_bin`
```c
FIO_SFUNC size_t fio_digits_bin(uint64_t i)
```
Returns the number of digits in base 2 for an unsigned number.
_Symbol type:_ `function`
#### `fio_digits_xbase`
```c
FIO_SFUNC size_t fio_digits_xbase(uint64_t i, size_t base)
```
Returns the number of digits in any base X<65 for an unsigned number.
_Symbol type:_ `function`
#### `fio_ltoa10`
```c
inline void fio_ltoa10(char *dest, int64_t i, size_t digits)
```
Writes a signed number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_atol10`
```c
int64_t fio_atol10(char **pstr)
```
Reads a signed base 10 formatted number.
_Symbol type:_ `function`
#### `fio_ltoa10u`
```c
inline void fio_ltoa10u(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa16u`
```c
inline void fio_ltoa16u(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa_bin`
```c
inline void fio_ltoa_bin(char *dest, uint64_t i, size_t digits)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_ltoa_xbase`
```c
inline void fio_ltoa_xbase(char *dest, uint64_t i, size_t digits, size_t base)
```
Writes unsigned number to `dest` using `digits` bytes (+ `NUL`)
_Symbol type:_ `function`
#### `fio_atol8u`
```c
uint64_t fio_atol8u(char **pstr)
```
Reads a signed base 8 formatted number - may overflow buffer.
_Symbol type:_ `function`
#### `fio_atol10u`
```c
uint64_t fio_atol10u(char **pstr)
```
Reads a signed base 10 formatted number - may overflow buffer.
_Symbol type:_ `function`
#### `fio_atol16u`
```c
uint64_t fio_atol16u(char **pstr)
```
Reads an unsigned hex formatted number (possibly prefixed with "0x").
_Symbol type:_ `function`
#### `fio_atol_bin`
```c
uint64_t fio_atol_bin(char **pstr)
```
Reads an unsigned binary formatted number (possibly prefixed with "0b").
_Symbol type:_ `function`
#### `fio_atol_xbase`
```c
uint64_t fio_atol_xbase(char **pstr, size_t base)
```
Read an unsigned number in any base up to base 36.
_Symbol type:_ `function`
#### `fio_stol10u`
```c
uint64_t fio_stol10u(char **pos, const char *end)
```
Reads an unsigned base 10 number within `[pos, end)` - strict digits only
(no whitespace, sign, underscores or prefixes). Advances `pos` past the
digits consumed. On overflow sets `errno == E2BIG` and stops at the last
valid digit. Callers detect empty / trailing junk by testing `pos`.
_Symbol type:_ `function`
#### `fio_stol10`
```c
int64_t fio_stol10(char **pos, const char *end)
```
Reads a signed base 10 number within `[pos, end)` - an optional leading
`-` / `+` followed by strict digits only. Sets `errno == E2BIG` on
overflow (either the magnitude or the signed range limit).
_Symbol type:_ `function`
#### `fio_stol16u`
```c
uint64_t fio_stol16u(char **pos, const char *end)
```
Reads an unsigned hex number within `[pos, end)` - strict hex digits
only (NO `0x` prefix, no underscores). Advances `pos` past the digits
consumed. On overflow sets `errno == E2BIG` and stops at the last valid
digit.
_Symbol type:_ `function`
#### `fio_u2i_limit`
```c
inline int64_t fio_u2i_limit(uint64_t val, size_t invert)
```
Converts an unsigned `val` to a signed `val`, with overflow protection.
_Symbol type:_ `function`
#### `fio_i2d`
```c
inline double fio_i2d(int64_t mant, int64_t exponent_in_base_2)
```
Converts a 64 bit integer to an IEEE 754 formatted double.
_Symbol type:_ `function`
#### `fio_u2d`
```c
inline double fio_u2d(uint64_t mant, int64_t exponent_in_base_2)
```
Converts a 64 bit unsigned integer to an IEEE 754 formatted double.
_Symbol type:_ `function`
#### `fio_u128_hex_read`
```c
fio_u128 fio_u128_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u256_hex_read`
```c
fio_u256 fio_u256_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u512_hex_read`
```c
fio_u512 fio_u512_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u1024_hex_read`
```c
fio_u1024 fio_u1024_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u2048_hex_read`
```c
fio_u2048 fio_u2048_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u4096_hex_read`
```c
fio_u4096 fio_u4096_hex_read(char **pstr)
```
Reads a hex numeral string and initializes the numeral.
_Symbol type:_ `function`
#### `fio_u128_hex_write`
```c
size_t fio_u128_hex_write(char *dest, const fio_u128 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u256_hex_write`
```c
size_t fio_u256_hex_write(char *dest, const fio_u256 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u512_hex_write`
```c
size_t fio_u512_hex_write(char *dest, const fio_u512 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u1024_hex_write`
```c
size_t fio_u1024_hex_write(char *dest, const fio_u1024 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u2048_hex_write`
```c
size_t fio_u2048_hex_write(char *dest, const fio_u2048 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_u4096_hex_write`
```c
size_t fio_u4096_hex_write(char *dest, const fio_u4096 *)
```
Prints out the underlying 64 bit array (for debugging).
_Symbol type:_ `function`
#### `fio_ltoa8u`
```c
inline void fio_ltoa8u(char *dest, uint64_t i, size_t digits)
```
_Symbol type:_ `function`
#### `fio_d2expo`
```c
inline int fio_d2expo(double d)
```
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 crc32.h`
1 public symbols.
### Functions
#### `fio_crc32`
```c
uint32_t fio_crc32(const void *data, size_t len, uint32_t initial_crc)
```
Computes CRC32 (ITU-T V.42 / ISO 3309 / gzip polynomial 0xEDB88320).
`data` - pointer to input bytes.
`len` - number of bytes to process.
`initial_crc` - pass 0 for a new computation, or a previous return value
to continue an incremental CRC32 over multiple buffers.
Returns the CRC32 checksum.
Uses slicing-by-16 internally (~8-16x faster than byte-at-a-time).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 glob matching.h`
1 public symbols.
### Functions
#### `fio_glob_match`
```c
uint8_t fio_glob_match(fio_str_info_s pattern, fio_str_info_s string)
```
A binary glob matching helper. Returns 1 on match, otherwise returns 0.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 imap.h`
7 public symbols.
### Macros
#### `FIO_IMAP_ALWAYS_VALID`
```c
#define FIO_IMAP_ALWAYS_VALID(o) (1)
```
Helper macro for simple iMap array types - always valid.
_Symbol type:_ `macro`
#### `FIO_IMAP_VALID_NON_ZERO`
```c
#define FIO_IMAP_VALID_NON_ZERO(o) (!!((o)[0]))
```
Helper macro for simple iMap array types - valid if nonzero.
_Symbol type:_ `macro`
#### `FIO_IMAP_ALWAYS_CMP_TRUE`
```c
#define FIO_IMAP_ALWAYS_CMP_TRUE(a, b) (1)
```
Helper macro for simple iMap array types - type comparison always true.
_Symbol type:_ `macro`
#### `FIO_IMAP_ALWAYS_CMP_FALSE`
```c
#define FIO_IMAP_ALWAYS_CMP_FALSE(a, b) (0)
```
Helper macro for simple iMap array types - type comparison always false.
_Symbol type:_ `macro`
#### `FIO_IMAP_SIMPLE_CMP`
```c
#define FIO_IMAP_SIMPLE_CMP(a, b) ((a)[0] == (b)[0])
```
Helper macro for simple iMap array types - simple comparison.
_Symbol type:_ `macro`
#### `FIO_IMAP_EACH`
```c
#define FIO_IMAP_EACH(array_name, map_ptr, i) \
for (size_t i = 0; i < (map_ptr)->w; ++i) \
if (!FIO_NAME(array_name, is_valid)((map_ptr)->ary + i)) \
continue; \
else
```
Helper macro for simple iMap array types.
_Symbol type:_ `macro`
#### `FIO_TYPEDEF_IMAP_ARRAY`
```c
#define FIO_TYPEDEF_IMAP_ARRAY(array_name, \
array_type, \
imap_type, \
hash_fn, \
cmp_fn, \
is_valid_fn) \
FIO_LEAK_COUNTER_DEF(FIO_NAME(array_name, s)) \
typedef struct { \
array_type *ary; \
imap_type count; \
imap_type w; \
uint32_t capa_bits; \
} FIO_NAME(array_name, s); \
typedef struct { \
imap_type pos; \
imap_type ipos; \
imap_type set_val; \
} FIO_NAME(array_name, seeker_s); \
/** Returns the theoretical capacity for the indexed array. */ \
FIO_IFUNC int FIO_NAME(array_name, is_valid)(array_type * pobj) { \
return pobj && (!!is_valid_fn(pobj)); \
} \
/** Returns the theoretical capacity for the indexed array. */ \
FIO_IFUNC size_t FIO_NAME(array_name, capa)(FIO_NAME(array_name, s) * a) { \
if (!a || !a->capa_bits) \
return 0; \
return ((size_t)1ULL << a->capa_bits); \
} \
/** Returns a pointer to the index map. */ \
FIO_IFUNC imap_type *FIO_NAME(array_name, \
imap)(FIO_NAME(array_name, s) * a) { \
return (imap_type *)(a->ary + ((imap_type)1ULL << a->capa_bits)); \
} \
/** Deallocates dynamic memory. */ \
FIO_IFUNC void FIO_NAME(array_name, destroy)(FIO_NAME(array_name, s) * a) { \
size_t capa = FIO_NAME(array_name, capa)(a); \
if (a->ary) { \
FIO_LEAK_COUNTER_ON_FREE(FIO_NAME(array_name, s)); \
FIO_TYPEDEF_IMAP_FREE( \
a->ary, \
(capa * (sizeof(*a->ary)) + (capa * (sizeof(imap_type))))); \
} \
*a = (FIO_NAME(array_name, s)){0}; \
(void)capa; /* if unused */ \
} \
/** Allocates dynamic memory. */ \
FIO_IFUNC int FIO_NAME(array_name, __alloc)(FIO_NAME(array_name, s) * a, \
size_t bits) { \
if (!bits || bits > ((sizeof(imap_type) << 3) - 2)) \
return -1; \
size_t capa = 1ULL << bits; \
if (bits > (size_t)(FIO_MAP_WARNING_BITSIZE - 1)) \
FIO_LOG_WARNING( \
"The " #array_name "_s map is now using a LOT of memory - %zu Mb!", \
(capa >> 20) * (sizeof(array_type) + sizeof(imap_type))); \
size_t old_capa = FIO_NAME(array_name, capa)(a); \
array_type *tmp = (array_type *)FIO_TYPEDEF_IMAP_REALLOC( \
a->ary, \
(a->capa_bits ? (old_capa * (sizeof(array_type)) + \
(old_capa * (sizeof(imap_type)))) \
: 0), \
(capa * (sizeof(array_type)) + (capa * (sizeof(imap_type)))), \
(a->w * (sizeof(array_type)))); \
(void)old_capa; /* if unused */ \
if (!tmp) \
return -1; \
if (!a->ary) \
FIO_LEAK_COUNTER_ON_ALLOC(FIO_NAME(array_name, s)); \
a->capa_bits = (uint32_t)bits; \
a->ary = tmp; \
if (!FIO_TYPEDEF_IMAP_REALLOC_IS_SAFE) { \
FIO_MEMSET((tmp + a->w), 0, ((capa - a->w) * (sizeof(*tmp)))); \
FIO_MEMSET((tmp + capa), 0, (capa * (sizeof(imap_type)))); \
} \
return 0; \
} \
/** Returns the index map position and array position of a value, if any. */ \
FIO_SFUNC FIO_NAME(array_name, seeker_s) \
FIO_NAME(array_name, seek)(FIO_NAME(array_name, s) * a, \
array_type * pobj) { \
FIO_NAME(array_name, seeker_s) \
r = {0, ((imap_type) ~(imap_type)0), ((imap_type) ~(imap_type)0)}; \
if (!a || ((!a->capa_bits) | (!a->ary))) \
return r; \
r.pos = a->w; \
imap_type capa = (imap_type)1UL << a->capa_bits; \
imap_type *imap = (imap_type *)(a->ary + capa); \
const imap_type pos_mask = (imap_type)(capa - (imap_type)1); \
const imap_type hash_mask = (imap_type)~pos_mask; \
const imap_type hash = (imap_type)hash_fn(pobj); \
imap_type tester = (hash & hash_mask); /* hides lower bits for `tester` */ \
imap_type pos = hash + (hash >> a->capa_bits); /* use more bits for pos */ \
tester += (imap_type)((!tester) << a->capa_bits); \
tester -= (imap_type)((hash_mask == tester) << a->capa_bits); \
size_t attempts = 11; \
for (;;) { \
/* tests up to 3 groups of 4 bytes (uint32_t) within a 64 byte group */ \
for (size_t mini_steps = 0;;) { \
pos &= pos_mask; \
const imap_type pos_hash = imap[pos] & hash_mask; \
const imap_type pos_index = imap[pos] & pos_mask; \
if ((pos_hash == tester) && cmp_fn((a->ary + pos_index), pobj)) { \
r.ipos = pos; \
r.pos = pos_index; \
r.set_val = tester | pos_index; \
return r; \
} \
if (!imap[pos]) { \
r.ipos = pos; \
r.set_val = tester | r.pos; /* r.pos == a->w */ \
return r; \
} \
if (imap[pos] == (imap_type)(~(imap_type)0)) { \
r.ipos = pos; \
r.set_val = tester | r.pos; /* r.pos == a->w */ \
} \
if (!((--attempts))) \
return r; \
if (mini_steps == 2) \
break; \
pos += 3 + mini_steps; /* 0, 3, 7 = max of 56 byte distance */ \
++mini_steps; \
} \
pos += (imap_type)0xC19F5985UL; /* big step */ \
} \
} \
/** fills an empty imap with the info about existing elements. */ \
FIO_SFUNC int FIO_NAME(array_name, \
__fill_imap)(FIO_NAME(array_name, s) * a) { \
if (!a->count) { \
a->w = 0; \
return 0; \
} \
imap_type *imap = FIO_NAME(array_name, imap)(a); \
if (a->count != a->w) { \
a->count = 0; \
for (size_t i = 0; i < a->w; ++i) { \
if (!is_valid_fn((a->ary + i))) \
continue; \
if (a->count != i) \
a->ary[a->count] = a->ary[i]; \
++a->count; \
} \
} \
for (a->w = 0; a->w < a->count; ++(a->w)) { \
FIO_NAME(array_name, seeker_s) \
s = FIO_NAME(array_name, seek)(a, a->ary + a->w); \
if (s.pos != a->w || s.ipos == (imap_type)(~(imap_type)0)) { \
a->w = a->count; \
return -1; /* destination not big enough to contain collisions! */ \
} \
imap[s.ipos] = s.set_val; \
} \
a->w = a->count; \
return 0; \
} \
/** expands the existing array & imap storage capacity. */ \
FIO_IFUNC int FIO_NAME(array_name, __expand)(FIO_NAME(array_name, s) * a) { \
for (;;) { \
if (FIO_NAME(array_name, __alloc)(a, \
a->capa_bits + 1 + (!a->capa_bits))) \
return -1; \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return 0; \
} \
} \
/** Reserves a minimum imap storage capacity. */ \
FIO_IFUNC int FIO_NAME(array_name, reserve)(FIO_NAME(array_name, s) * a, \
imap_type min) { \
imap_type bits = 2; \
if (min > ((imap_type)~0ULL) >> 1) \
return -1; \
while ((1ULL << bits) < min) \
++bits; \
if (bits <= a->capa_bits) \
return 0; \
if (FIO_NAME(array_name, __alloc)(a, bits)) \
return -1; \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return 0; \
return FIO_NAME(array_name, __expand)(a); \
} \
/** Rehashes the array and fills the imap (use after sorting). */ \
FIO_IFUNC int FIO_NAME(array_name, rehash)(FIO_NAME(array_name, s) * a) { \
if (!a || !a->ary) \
return -1; \
size_t bytes = sizeof(imap_type) * ((size_t)1ULL << a->capa_bits); \
imap_type *imap = FIO_NAME(array_name, imap)(a); \
FIO_MEMSET(imap, 0, bytes); \
if (!FIO_NAME(array_name, __fill_imap)(a)) \
return -1; \
return 0; \
} \
/** Sets an object in the Array. Optionally overwrites existing data. */ \
FIO_IFUNC array_type *FIO_NAME(array_name, set)(FIO_NAME(array_name, s) * a, \
array_type obj, \
int overwrite) { \
if (!a || !is_valid_fn((&obj))) \
return NULL; \
{ \
size_t capa = FIO_NAME(array_name, capa)(a); \
if (a->w == capa) \
FIO_NAME(array_name, __expand)(a); \
else if (a->count != a->w && \
(a->w + (a->w >> 1)) > FIO_NAME(array_name, capa)(a)) { \
FIO_MEMSET((a->ary + capa), 0, (capa * (sizeof(imap_type)))); \
FIO_NAME(array_name, __fill_imap)(a); \
} \
} \
for (;;) { \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.ipos == (imap_type)(~(imap_type)0)) { /* no room in the imap */ \
FIO_NAME(array_name, __expand)(a); \
continue; \
} \
if (s.pos == a->w) { /* new object */ \
a->ary[a->w] = obj; \
++a->w; \
++a->count; \
FIO_NAME(array_name, imap)(a)[s.ipos] = s.set_val; \
return a->ary + s.pos; \
} \
FIO_ASSERT_DEBUG(s.pos < a->w && s.ipos < FIO_NAME(array_name, capa)(a), \
"WTF?"); \
if (!overwrite) \
return a->ary + s.pos; \
a->ary[s.pos] = obj; \
return a->ary + s.pos; \
} \
} \
/** Finds an object in the Array using the index map. */ \
FIO_IFUNC array_type *FIO_NAME(array_name, get)(FIO_NAME(array_name, s) * a, \
array_type obj) { \
if (!a || !is_valid_fn((&obj))) \
return NULL; \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.pos >= a->w) \
return NULL; \
return a->ary + s.pos; \
} \
/** Removes an object in the Array's index map, zeroing out its memory. */ \
FIO_IFUNC int FIO_NAME(array_name, remove)(FIO_NAME(array_name, s) * a, \
array_type obj) { \
if (!a || !is_valid_fn((&obj))) \
return -1; \
FIO_NAME(array_name, seeker_s) s = FIO_NAME(array_name, seek)(a, &obj); \
if (s.pos >= a->w) \
return -1; \
a->ary[s.pos] = (array_type){0}; \
FIO_NAME(array_name, imap)(a)[s.ipos] = (imap_type)(~(imap_type)0); \
--a->count; \
while (a->w && !is_valid_fn((a->ary + a->w - 1))) \
--a->w; \
return 0; \
}
```
This MACRO defines the type and functions needed for an indexed array.
This is used internally and documentation is poor.
An indexed array is simple ordered array who's objects are indexed using an
almost-hash map, allowing for easy seeking while also enjoying an array's
advantages.
The index map uses one `imap_type` (i.e., `uint64_t`) to store both the array
index and any leftover hash data (the first half being tested during the
random access and the leftover during comparison). The reserved value `0`
indicates a free slot. The reserved value `~0` indicates a freed item (a free
slot that was previously used).
- `array_name_s` the main array container (.ary is the array itself)
- `array_name_seeker_s` is a seeker type that finds objects.
- `array_name_seek` finds an object or its future position.
- `array_name_capa` the imap's theoretical storage capacity.
- `array_name_set` writes or overwrites data to the array.
- `array_name_get` returns a pointer to the object within the array.
- `array_name_remove` removes an object and resets its memory to zero.
- `array_name_reserve` reserves a minimum imap storage capacity.
- `array_name_rehash` re-builds the imap (use after sorting).
- `array_name_destroy` de-allocates internal dynamic memory.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/002 math.h`
6 public symbols.
### Functions
#### `fio_math_div`
```c
inline void fio_math_div(uint64_t *dest, uint64_t *reminder, const uint64_t *a, const uint64_t *b, const size_t number_array_length)
```
Multi-precision DIV for `len*64` bit long a, b.
This is NOT constant time.
The algorithm might be slow, as my math isn't that good and I couldn't
understand faster division algorithms (such as Newton–Raphson division)... so
this is sort of a factorized variation on long division.
_Symbol type:_ `function`
#### `fio_math_shr`
```c
inline void fio_math_shr(uint64_t *dest, uint64_t *n, const size_t right_shift_bits, size_t number_array_length)
```
Multi-precision shift right for `len` word number `n`.
_Symbol type:_ `function`
#### `fio_math_shl`
```c
inline void fio_math_shl(uint64_t *dest, uint64_t *n, const size_t left_shift_bits, const size_t number_array_length)
```
Multi-precision shift left for `len*64` bit number `n`.
_Symbol type:_ `function`
#### `fio_math_inv`
```c
inline void fio_math_inv(uint64_t *dest, uint64_t *n, size_t len)
```
Multi-precision Inverse for `len*64` bit number `n` (turn `1` into `-1`).
_Symbol type:_ `function`
#### `fio_math_msb_index`
```c
FIO_MIFN size_t fio_math_msb_index(uint64_t *n, const size_t len)
```
Multi-precision - returns the index for the most significant bit or -1.
_Symbol type:_ `function`
#### `fio_math_lsb_index`
```c
FIO_MIFN size_t fio_math_lsb_index(uint64_t *n, const size_t len)
```
Multi-precision - returns the index for the least significant bit or -1.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 random.h`
15 public symbols.
### Macros
#### `FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH`
```c
#define FIO_USE_STABLE_HASH_WHEN_CALLING_RISKY_HASH 0
```
_Symbol type:_ `macro`
### Functions
#### `fio_rand64`
```c
uint64_t fio_rand64(void)
```
Returns 64 psedo-random bits. Probably not cryptographically safe.
_Symbol type:_ `function`
#### `fio_rand128`
```c
fio_u128 fio_rand128(void)
```
Returns 64 psedo-random bits. Probably not cryptographically safe.
_Symbol type:_ `function`
#### `fio_rand_bytes`
```c
void fio_rand_bytes(void *target, size_t len)
```
Writes `len` bytes of psedo-random bits to the target buffer.
_Symbol type:_ `function`
#### `fio_rand_bytes_secure`
```c
int fio_rand_bytes_secure(void *target, size_t len)
```
Writes `len` bytes of cryptographically secure random data to `target`.
Uses the system CSPRNG: arc4random_buf() on BSD/macOS, BCryptGenRandom() on
Windows (MSVC links bcrypt.lib automatically; MinGW must link `-lbcrypt`),
getrandom() on Linux, or /dev/urandom as fallback.
Returns 0 on success, -1 on failure (never partial success; on failure the
`target` content MUST NOT be used). A NULL `target` with `len > 0` fails.
IMPORTANT: Use this for security-sensitive operations like key generation.
_Symbol type:_ `function`
#### `fio_rand_reseed`
```c
void fio_rand_reseed(void)
```
Reseeds the random engine using system state (rusage / jitter).
_Symbol type:_ `function`
#### `fio_risky_hash`
```c
uint64_t fio_risky_hash(const void *buf, size_t len, uint64_t seed)
```
Computes a facil.io Risky Hash (Risky v.3).
_Symbol type:_ `function`
#### `fio_risky_ptr`
```c
inline uint64_t fio_risky_ptr(void *ptr)
```
Adds a bit of entropy to pointer values. Designed to be unsafe.
_Symbol type:_ `function`
#### `fio_risky_num`
```c
inline uint64_t fio_risky_num(uint64_t number, uint64_t seed)
```
Adds a bit of entropy to numeral values. Designed to be unsafe.
_Symbol type:_ `function`
#### `fio_stable_hash`
```c
uint64_t fio_stable_hash(const void *data, size_t len, uint64_t seed)
```
Computes a facil.io Stable Hash (will not be updated, even if broken).
_Symbol type:_ `function`
#### `fio_stable_hash128`
```c
void fio_stable_hash128(void *restrict dest, const void *restrict data, size_t len, uint64_t seed)
```
Computes a facil.io Stable Hash (will not be updated, even if broken).
_Symbol type:_ `function`
#### `fio_risky256`
```c
fio_u256 fio_risky256(const void *data, uint64_t len)
```
Computes a 256-bit non-cryptographic hash (RiskyHash 256).
Based on the A3 (Zero-Copy ILP) design: 2-state multiply-fold with
cross-lane mixing, 128 bytes/iteration, zero-copy XOR from input.
Returns a `fio_u256` (32 bytes). Passes strict avalanche (50.0%),
collision, differential, and length independence tests.
_Symbol type:_ `function`
#### `fio_risky512`
```c
fio_u512 fio_risky512(const void *data, uint64_t len)
```
Computes a 512-bit non-cryptographic hash (RiskyHash 512).
SHAKE-style extension of fio_risky256: the first 256 bits of the 512-bit
output are identical to fio_risky256 (truncation-safe). The second 256 bits
come from an additional squeeze round.
Returns a `fio_u512` (64 bytes).
_Symbol type:_ `function`
#### `fio_risky256_hmac`
```c
fio_u256 fio_risky256_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
Computes HMAC-RiskyHash-256 (RFC 2104 construction, 32-byte digest).
Uses fio_risky256 as the underlying hash with a 64-byte block size.
If `key_len > 64`, the key is hashed first with fio_risky256.
NOTE: The underlying hash is non-cryptographic, so standard HMAC
security proofs do not apply. For cryptographic HMAC, use blake2.
_Symbol type:_ `function`
#### `fio_risky512_hmac`
```c
fio_u512 fio_risky512_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
Computes HMAC-RiskyHash-512 (RFC 2104 construction, 64-byte digest).
Uses fio_risky512 as the underlying hash with a 64-byte block size.
If `key_len > 64`, the key is hashed first with fio_risky512.
NOTE: The underlying hash is non-cryptographic, so standard HMAC
security proofs do not apply. For cryptographic HMAC, use blake2.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 signals.h`
8 public symbols.
### Macros
#### `FIO_SIGNAL_USER1`
```c
#define FIO_SIGNAL_USER1 SIGUSR1
```
_Symbol type:_ `macro`
#### `FIO_SIGNAL_USER2`
```c
#define FIO_SIGNAL_USER2 SIGUSR2
```
_Symbol type:_ `macro`
#### `FIO_SIGNAL_USER_UNREGISTERED`
```c
#define FIO_SIGNAL_USER_UNREGISTERED SIGUSR2
```
_Symbol type:_ `macro`
### Types
#### `fio_signal_monitor_args_s`
```c
typedef struct {
/** The signal number to listen for. */
int sig;
/** The callback to run - leave NULL to ignore signal. */
void (*callback)(int sig, void *udata);
/** Opaque user data. */
void *udata;
/** Should the signal propagate to existing handler(s)? */
bool propagate;
/** Call (safe) callback immediately? or wait for `fio_signal_review`? */
bool immediate;
} fio_signal_monitor_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_signal_monitor`
```c
int fio_signal_monitor(fio_signal_monitor_args_s args)
```
Starts to monitor for the specified signal, setting an optional callback.
_Symbol type:_ `function`
#### `fio_signal_monitor`
```c
#define fio_signal_monitor(...) \
fio_signal_monitor((fio_signal_monitor_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_signal_review`
```c
int fio_signal_review(void)
```
Reviews all signals, calling any relevant callbacks.
_Symbol type:_ `function`
#### `fio_signal_forget`
```c
int fio_signal_forget(int sig)
```
Stops monitoring the specified signal.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 sort.h`
3 public symbols.
### Macros
#### `FIO_SORT_THRESHOLD`
```c
#define FIO_SORT_THRESHOLD 96
```
The default threshold below which quicksort delegates to insert sort.
_Symbol type:_ `macro`
#### `FIO_SORT_SWAP`
```c
#define FIO_SORT_SWAP(a, b) \
do { \
FIO_SORT_TYPE tmp__ = (a); \
(a) = (b); \
(b) = tmp__; \
} while (0)
```
Default swap operation assumes an array and swaps array members
_Symbol type:_ `macro`
#### `FIO_SORT_IS_BIGGER`
```c
#define FIO_SORT_IS_BIGGER(a, b) ((a) > (b))
```
MUST evaluate as 1 if a > b (zero if equal or smaller).
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/002 threads.h`
33 public symbols.
### Macros
#### `FIO_THREAD_MUTEX_INIT`
```c
#define FIO_THREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
```
Used this macro for static initialization.
_Symbol type:_ `macro`
#### `FIO_MEMCPY_THREADS`
```c
#define FIO_MEMCPY_THREADS 8
```
_Symbol type:_ `macro`
### Types
#### `fio_thread_t`
```c
typedef pthread_t fio_thread_t
```
_Symbol type:_ `type`
#### `fio_thread_pid_t`
```c
typedef pid_t fio_thread_pid_t
```
_Symbol type:_ `type`
#### `fio_thread_mutex_t`
```c
typedef pthread_mutex_t fio_thread_mutex_t
```
_Symbol type:_ `type`
#### `fio_thread_cond_t`
```c
typedef pthread_cond_t fio_thread_cond_t
```
_Symbol type:_ `type`
#### `fio_thread_priority_e`
```c
typedef enum {
FIO_THREAD_PRIORITY_ERROR = -1,
FIO_THREAD_PRIORITY_LOWEST = 0,
FIO_THREAD_PRIORITY_LOW,
FIO_THREAD_PRIORITY_NORMAL,
FIO_THREAD_PRIORITY_HIGH,
FIO_THREAD_PRIORITY_HIGHEST,
} fio_thread_priority_e
```
Possible thread priority values.
_Symbol type:_ `type`
### Functions
#### `fio_thread_fork`
```c
inline fio_thread_pid_t fio_thread_fork(void)
```
Should behave the same as the POSIX system call `fork`.
_Symbol type:_ `function`
#### `fio_thread_getpid`
```c
inline fio_thread_pid_t fio_thread_getpid(void)
```
Should behave the same as the POSIX system call `getpid`.
_Symbol type:_ `function`
#### `fio_thread_kill`
```c
inline int fio_thread_kill(fio_thread_pid_t pid, int sig)
```
Should behave the same as the POSIX system call `kill`.
_Symbol type:_ `function`
#### `fio_thread_waitpid`
```c
inline int fio_thread_waitpid(fio_thread_pid_t pid, int *stat_loc, int options)
```
Should behave the same as the POSIX system call `waitpid`.
_Symbol type:_ `function`
#### `fio_thread_create`
```c
inline int fio_thread_create(fio_thread_t *t, void *(*fn)(void *), void *arg)
```
Starts a new thread, returns 0 on success and -1 on failure.
_Symbol type:_ `function`
#### `fio_thread_join`
```c
inline int fio_thread_join(fio_thread_t *t)
```
Waits for the thread to finish.
_Symbol type:_ `function`
#### `fio_thread_detach`
```c
inline int fio_thread_detach(fio_thread_t *t)
```
Detaches the thread, so thread resources are freed automatically.
_Symbol type:_ `function`
#### `fio_thread_exit`
```c
inline void fio_thread_exit(void)
```
Ends the current running thread.
_Symbol type:_ `function`
#### `fio_thread_equal`
```c
inline int fio_thread_equal(fio_thread_t *a, fio_thread_t *b)
```
_Symbol type:_ `function`
#### `fio_thread_current`
```c
inline fio_thread_t fio_thread_current(void)
```
Returns the current thread.
_Symbol type:_ `function`
#### `fio_thread_nid`
```c
inline uintptr_t fio_thread_nid(void)
```
Returns a process-local numeral ID for the current thread.
The value is stable for the thread's lifetime and unique among live threads.
It may be reused after the thread exits.
_Symbol type:_ `function`
#### `fio_thread_yield`
```c
inline void fio_thread_yield(void)
```
Yields thread execution.
_Symbol type:_ `function`
#### `fio_thread_priority`
```c
FIO_SFUNC fio_thread_priority_e fio_thread_priority(void)
```
Returns a thread's priority level.
_Symbol type:_ `function`
#### `fio_thread_priority_set`
```c
FIO_SFUNC int fio_thread_priority_set(fio_thread_priority_e)
```
Sets a thread's priority level.
_Symbol type:_ `function`
#### `fio_thread_mutex_init`
```c
inline int fio_thread_mutex_init(fio_thread_mutex_t *m)
```
Initializes a simple Mutex.
Or use the static initialization value: FIO_THREAD_MUTEX_INIT
_Symbol type:_ `function`
#### `fio_thread_mutex_lock`
```c
inline int fio_thread_mutex_lock(fio_thread_mutex_t *m)
```
Locks a simple Mutex, returning -1 on error.
_Symbol type:_ `function`
#### `fio_thread_mutex_trylock`
```c
inline int fio_thread_mutex_trylock(fio_thread_mutex_t *m)
```
Attempts to lock a simple Mutex, returning zero on success.
_Symbol type:_ `function`
#### `fio_thread_mutex_unlock`
```c
inline int fio_thread_mutex_unlock(fio_thread_mutex_t *m)
```
Unlocks a simple Mutex, returning zero on success or -1 on error.
_Symbol type:_ `function`
#### `fio_thread_mutex_destroy`
```c
inline void fio_thread_mutex_destroy(fio_thread_mutex_t *m)
```
Destroys the simple Mutex (cleanup).
_Symbol type:_ `function`
#### `fio_thread_cond_init`
```c
inline int fio_thread_cond_init(fio_thread_cond_t *c)
```
Initializes a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_wait`
```c
inline int fio_thread_cond_wait(fio_thread_cond_t *c, fio_thread_mutex_t *m)
```
Waits on a conditional variable (MUST be previously locked).
_Symbol type:_ `function`
#### `fio_thread_cond_timedwait`
```c
inline int fio_thread_cond_timedwait(fio_thread_cond_t *c, fio_thread_mutex_t *m, size_t milliseconds)
```
Waits on a conditional variable (MUST be previously locked).
_Symbol type:_ `function`
#### `fio_thread_cond_signal`
```c
inline int fio_thread_cond_signal(fio_thread_cond_t *c)
```
Signals a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_broadcast`
```c
inline int fio_thread_cond_broadcast(fio_thread_cond_t *c)
```
Signal broadcast for a conditional variable.
_Symbol type:_ `function`
#### `fio_thread_cond_destroy`
```c
inline void fio_thread_cond_destroy(fio_thread_cond_t *c)
```
Destroys a simple conditional variable.
_Symbol type:_ `function`
#### `fio_thread_memcpy`
```c
FIO_SFUNC size_t fio_thread_memcpy(void *restrict dest, const void *restrict src, size_t bytes)
```
Multi-threaded memcpy using up to FIO_MEMCPY_THREADS threads
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/002 url.h`
7 public symbols.
### Macros
#### `FIO_URL_QUERY_EACH`
```c
#define FIO_URL_QUERY_EACH(query_buf, i) \
for (fio_url_query_each_s i = fio_url_query_each_next( \
(fio_url_query_each_s){.private___ = (query_buf)}); \
i.name.buf; \
i = fio_url_query_each_next(i))
```
Iterates through each of the query elements.
_Symbol type:_ `macro`
### Types
#### `fio_url_s`
```c
typedef struct {
fio_buf_info_s scheme;
fio_buf_info_s user;
fio_buf_info_s password;
fio_buf_info_s host;
fio_buf_info_s port;
fio_buf_info_s path;
fio_buf_info_s query;
fio_buf_info_s target;
} fio_url_s
```
the result returned by `fio_url_parse`
_Symbol type:_ `type`
#### `fio_url_query_each_s`
```c
typedef struct {
fio_buf_info_s name;
fio_buf_info_s value;
fio_buf_info_s private___;
} fio_url_query_each_s
```
The type used by the `FIO_URL_QUERY_EACH` iterator macro.
_Symbol type:_ `type`
#### `fio_url_tls_info_s`
```c
typedef struct {
fio_buf_info_s key;
fio_buf_info_s cert;
fio_buf_info_s trust;
fio_buf_info_s pass;
bool tls;
} fio_url_tls_info_s
```
_Symbol type:_ `type`
### Functions
#### `fio_url_parse`
```c
fio_url_s fio_url_parse(const char *url, size_t len)
```
Parses the URI returning it's components and their lengths (no decoding
performed, doesn't accept decoded URIs).
The returned string are NOT NUL terminated, they are merely locations within
the original string.
This function attempts to accept many different formats, including any of the
following:
* `/complete_path?query#target`
i.e.: /index.html?page=1#list
* `host:port/complete_path?query#target`
i.e.:
example.com
example.com:8080
example.com/index.html
example.com:8080/index.html
example.com:8080/index.html?key=val#target
* `user:password@host:port/path?query#target`
i.e.: user:1234@example.com:8080/index.html
* `username[:password]@host[:port][...]`
i.e.: john:1234@example.com
* `schema://user:password@host:port/path?query#target`
i.e.: http://example.com/index.html?page=1#list
For performance reasons, no format validation is performed. Function assumes
that the `url` string is `len` long and contains a valid URL. Invalid formats
might produce unexpected results.
NOTE: the `unix`, `file` and `priv` schemas are reserved for file paths.
_Symbol type:_ `function`
#### `fio_url_query_each_next`
```c
FIO_SFUNC fio_url_query_each_s fio_url_query_each_next(fio_url_query_each_s)
```
A helper function for the `FIO_URL_QUERY_EACH` macro implementation.
_Symbol type:_ `function`
#### `fio_url_is_tls`
```c
fio_url_tls_info_s fio_url_is_tls(fio_url_s u)
```
Returns TLS data associated with the URL.
This function supports implicit TLS by scheme data for the following possible
values:
- `wss` - Secure WebSockets.
- `sses` - Secure SSE (Server Sent Events).
- `https` - Secure HTTP.
- `tcps` - Secure TCP/IP.
- `tls` - Secure TCP/IP.
- `udps` - Secure UDP.
i.e.:
tls://example.com/
tcps://example.com/
udps://example.com/
wss://example.com/
https://example.com/
sses://example.com/
This function also supports explicit TLS by query data for the following
possible key-pair values:
- `tls` - self-signed TLS (unless key / cert are provided).
- `tls=true` - self-signed TLS (unless key / cert are provided).
- `tls=` - key and certificate files (same path, different
file extensions).
- `key=` - path or env variable name for the private key.
- `cert=` - path or env variable name for the public
certificate.
- `pass` - password for decrypting key / cert data.
i.e.:
tcp://example.com/?tls (anonymous TLS)
udp://example.com/?tls=true
https://example.com/?tls=key_cert_folder_or_prefix&pass=key_password
https://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
wss://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
tcp://example.com/?key=key_file_or_env_var&cert=cert_file_or_env_var&pass=key_password
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/003 entities.h`
1 public symbols.
### Functions
#### `fio_entity`
```c
size_t fio_entity(char *dest, const char *src, size_t len)
```
Decodes a single ML entity from `src` (length `len`, starting at '&').
Writes decoded UTF-8 bytes to `dest`, which MUST be at least 8 bytes.
A NUL terminator is written only if the decoded result is shorter than
8 bytes.
Returns the number of bytes written to `dest` (excluding NUL), or 0 if
`src` does not start with a valid entity.
Supported forms:
- Named: `&name;` (case-insensitive, ~40 common entities)
- Decimal: `digits;`
- Hex: `hex;` or `hex;`
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 files.h`
28 public symbols.
### Macros
#### `FIO_FILENAME_PATH_CAPA`
```c
#define FIO_FILENAME_PATH_CAPA (PATH_MAX | 4096)
```
_Symbol type:_ `macro`
#### `FIO_FD_FIND_EOF`
```c
#define FIO_FD_FIND_EOF ((size_t)-1)
```
End of file value for `fio_fd_find_next`
_Symbol type:_ `macro`
#### `FIO_FD_FIND_BLOCK`
```c
#define FIO_FD_FIND_BLOCK 4096
```
Size on the stack used by `fio_fd_find_next` for each read cycle.
_Symbol type:_ `macro`
#### `FIO_FOLDER_SEPARATOR`
```c
#define FIO_FOLDER_SEPARATOR '\\'
```
_Symbol type:_ `macro`
### Types
#### `fio_filename_remove_args_s`
```c
typedef struct {
/** The path of the file / folder to remove. */
const char *path;
/** Set to allow the removal of an (empty) folder. */
uint8_t folder;
/** Set to remove a folder and all of its content (implies `folder`). */
uint8_t recursive;
} fio_filename_remove_args_s
```
Arguments for `fio_filename_remove`.
_Symbol type:_ `type`
#### `fio_filename_make_path_args_s`
```c
typedef struct {
/** The folder path to create (nested folders allowed). */
const char *path;
/** The creation mode (POSIX only); zero (0) defaults to 0755. */
uint32_t mode;
} fio_filename_make_path_args_s
```
Arguments for `fio_filename_make_path`.
_Symbol type:_ `type`
#### `fio_filename_s`
```c
typedef struct {
fio_buf_info_s folder;
fio_buf_info_s basename;
fio_buf_info_s ext;
} fio_filename_s
```
A result type for the filename parsing helper.
_Symbol type:_ `type`
### Functions
#### `fio_filename_open`
```c
int fio_filename_open(const char *filename, int flags)
```
Opens `filename`, returning the same as values as `open` on POSIX systems.
If `path` starts with a `"~/"` than it will be relative to the user's home
folder (on Windows, testing for `"~\"`).
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
_Symbol type:_ `function`
#### `fio_filename_is_unsafe`
```c
int fio_filename_is_unsafe(const char *path)
```
Returns 1 if `path` does folds backwards (OS separator dependent).
_Symbol type:_ `function`
#### `fio_filename_is_unsafe_url`
```c
int fio_filename_is_unsafe_url(const char *path)
```
Returns 1 if `path` does folds backwards (has "/../" or "//").
_Symbol type:_ `function`
#### `fio_filename_tmp`
```c
int fio_filename_tmp(void)
```
Creates a temporary file, returning its file descriptor.
_Symbol type:_ `function`
#### `fio_filename_remove`
```c
int fio_filename_remove(fio_filename_remove_args_s args)
```
Removes the file / link or folder at `path`.
* By default (no flags), removes a file / link (like `unlink`).
* With `folder` set, removes an empty folder (like `rmdir`).
* With `recursive` set (implies `folder`), removes a folder and all of its
content (like `rm -r`). If `path` isn't a folder, the `recursive` flag
is ignored and `path` is removed as a file / link.
Links are removed, never followed into.
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
Returns 0 on success and -1 on error. Recursive removal stops on the first
error (some content may remain).
_Symbol type:_ `function`
#### `fio_filename_remove`
```c
#define fio_filename_remove(...) \
fio_filename_remove((fio_filename_remove_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_filename_make_path`
```c
int fio_filename_make_path(fio_filename_make_path_args_s args)
```
Creates the folder at `path`, including any missing parent folders
(similar to `mkdir -p`).
An existing folder is NOT an error. A non-folder component along the way IS
an error (`errno == ENOTDIR`).
Uses a fixed size stack buffer (zero allocations); over-long paths fail
with `errno == ENAMETOOLONG` (see `FIO_FILENAME_PATH_CAPA`).
Returns 0 on success and -1 on error.
_Symbol type:_ `function`
#### `fio_filename_make_path`
```c
#define fio_filename_make_path(...) \
fio_filename_make_path((fio_filename_make_path_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_filename_overwrite`
```c
inline int fio_filename_overwrite(const char *filename, const void *buf, size_t len)
```
Overwrites `filename` with the data in the buffer.
If `path` starts with a `"~/"` than it will be relative to the user's home
folder (on Windows, testing for `"~\"`).
Returns -1 on error or 0 on success. On error, the state of the file is
undefined (may be doesn't exit / nothing written / partially written).
_Symbol type:_ `function`
#### `fio_filename_size`
```c
inline size_t fio_filename_size(const char *filename)
```
Returns the file size (or 0 on both error / empty file).
_Symbol type:_ `function`
#### `fio_fd_size`
```c
inline size_t fio_fd_size(int fd)
```
Returns the file size (or 0 on both error / empty file).
_Symbol type:_ `function`
#### `fio_filename_type`
```c
inline size_t fio_filename_type(const char *filename)
```
Returns the file type (or 0 on both error).
See: https://www.man7.org/linux/man-pages/man7/inode.7.html
_Symbol type:_ `function`
#### `fio_filename_stat`
```c
inline int fio_filename_stat(const char *filename, struct stat *stat_buf)
```
Populates `stat_buf` with the file's metadata. Returns 0 on success.
_Symbol type:_ `function`
#### `fio_fd_type`
```c
inline size_t fio_fd_type(int fd)
```
Returns the file type (or 0 on both error).
See: https://www.man7.org/linux/man-pages/man7/inode.7.html
_Symbol type:_ `function`
#### `fio_filename_is_folder`
```c
#define fio_filename_is_folder(filename) \
(fio_filename_type((filename)) == S_IFDIR)
```
Tests if `filename` references a folder. Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_fd_write`
```c
inline ssize_t fio_fd_write(int fd, const void *buf, size_t len)
```
Writes data to a file handle, returning the number of bytes written.
Returns -1 on error.
Since some systems have a limit on the number of bytes that can be written at
a time, this function fragments the system calls into smaller `write` blocks,
allowing large data to be written.
If the file descriptor is non-blocking, test errno for EAGAIN / EWOULDBLOCK.
_Symbol type:_ `function`
#### `fio_fd_read`
```c
inline size_t fio_fd_read(int fd, void *buf, size_t len, off_t start_at)
```
Reads up to `len` bytes from `fd`, returning the number of bytes read.
Returns 0 if no bytes were read or on error.
Since some systems have a limit on the number of bytes that can be read at
a time, this function fragments the system calls into smaller `read` blocks,
allowing large data to be read.
If the file descriptor is non-blocking, test errno for EAGAIN / EWOULDBLOCK.
_Symbol type:_ `function`
#### `fio_filename_parse`
```c
fio_filename_s fio_filename_parse(const char *filename)
```
Parses a file name to folder, base name and extension (zero-copy).
_Symbol type:_ `function`
#### `fio_filename_parse2`
```c
fio_filename_s fio_filename_parse2(const char *filename, size_t len)
```
Parses a file name to folder, base name and extension (zero-copy).
_Symbol type:_ `function`
#### `fio_fd_find_next`
```c
size_t fio_fd_find_next(int fd, char token, size_t start_at)
```
Returns offset for the next `token` in `fd`, or -1 if reached EOF.
This will use `FIO_FD_FIND_BLOCK` bytes on the stack to read the file in a
loop.
Pros: limits memory use and (re)allocations, easier overflow protection.
Cons: may be slower, as data will most likely be copied again from the file.
_Symbol type:_ `function`
#### `fio_file_dup`
```c
#define fio_file_dup(fd) _dup(fd)
```
Duplicates the file handle (int)
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/004 http1 parser.h`
3 public symbols.
### Macros
#### `FIO_HTTP1_PARSER_INIT`
```c
#define FIO_HTTP1_PARSER_INIT ((fio_http1_parser_s){0})
```
Initialization value for the parser
_Symbol type:_ `macro`
#### `FIO_HTTP1_PARSER_ERROR`
```c
#define FIO_HTTP1_PARSER_ERROR ((size_t)-1)
```
The error return value for fio_http1_parse.
_Symbol type:_ `macro`
#### `FIO_HTTP1_EXPECTED_CHUNKED`
```c
#define FIO_HTTP1_EXPECTED_CHUNKED ((size_t)(-2))
```
A return value for `fio_http1_expected` when chunked data is expected.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/004 json.h`
5 public symbols.
### Macros
#### `FIO_JSON_MAX_DEPTH`
```c
#define FIO_JSON_MAX_DEPTH 128
```
Maximum allowed JSON nesting level. MUST be less then 65536
Values above 65536 might cause the stack to overflow and cause a failure.
_Symbol type:_ `macro`
#### `FIO_JSON_USE_FIO_ATON`
```c
#define FIO_JSON_USE_FIO_ATON 0
```
_Symbol type:_ `macro`
### Types
#### `fio_json_parser_callbacks_s`
```c
typedef struct {
/** NULL object was detected. Returns new object as `void *`. */
void *(*on_null)(void *udata);
/** TRUE object was detected. Returns new object as `void *`. */
void *(*on_true)(void *udata);
/** FALSE object was detected. Returns new object as `void *`. */
void *(*on_false)(void *udata);
/** Number was detected (long long). Returns new object as `void *`. */
void *(*on_number)(void *udata, int64_t i);
/** Float was detected (double).Returns new object as `void *`. */
void *(*on_float)(void *udata, double f);
/** (escaped) String was detected. Returns a new String as `void *`. */
void *(*on_string)(void *udata, const void *start, size_t len);
/** (unescaped) String was detected. Returns a new String as `void *`. */
void *(*on_string_simple)(void *udata, const void *start, size_t len);
/** Dictionary was detected. Returns ctx to hash map or NULL on error. */
void *(*on_map)(void *udata, void *ctx, void *at);
/** Array was detected. Returns ctx to array or NULL on error. */
void *(*on_array)(void *udata, void *ctx, void *at);
/** Map entry detected. Returns non-zero on error. Owns key and value. */
int (*map_push)(void *udata, void *ctx, void *key, void *value);
/** Array entry detected. Returns non-zero on error. Owns value. */
int (*array_push)(void *udata, void *ctx, void *value);
/** Called when an array object (`ctx`) appears done. */
int (*array_finished)(void *udata, void *ctx);
/** Called when a map object (`ctx`) appears done. */
int (*map_finished)(void *udata, void *ctx);
/** Called when context is expected to be an array (i.e., fio_json_update). */
int (*is_array)(void *udata, void *ctx);
/** Called when context is expected to be a map (i.e., fio_json_update). */
int (*is_map)(void *udata, void *ctx);
/** Called for unused objects (e.g., key on error). Must free the object. */
void (*free_unused_object)(void *udata, void *ctx);
/** The JSON parsing encountered an error. Owns ctx, should free or return. */
void *(*on_error)(void *udata, void *ctx);
} fio_json_parser_callbacks_s
```
The JSON parser settings (callbacks).
**Ownership**: Callbacks that return `void *` objects transfer ownership to
the parser. The parser will either pass these objects to `map_push` /
`array_push` (transferring ownership to the container), or call
`free_unused_object` if the object is not used (e.g., on error or NULL map
key). The `on_error` callback receives ownership of any partial result.
_Symbol type:_ `type`
#### `fio_json_result_s`
```c
typedef struct {
void *ctx;
size_t stop_pos;
int err;
} fio_json_result_s
```
The JSON return type.
_Symbol type:_ `type`
### Functions
#### `fio_json_parse`
```c
fio_json_result_s fio_json_parse(fio_json_parser_callbacks_s *settings, void *udata, const char *json_string, const size_t len)
```
The facil.io JSON parser is a non-strict parser, with support for trailing
commas in collections, new-lines in strings, extended escape characters and
octal, hex and binary numbers.
The parser allows for streaming data and decouples the parsing process from
the resulting data-structure by calling static callbacks for JSON related
events.
Returns the number of bytes consumed before parsing stopped (due to either
error or end of data). Stops as close as possible to the end of the buffer or
once an object parsing was completed.
Buffer requirement (guard-byte contract): number / quote-less key scanning
may read the byte at `json_string[len]` while deciding a token ended. The
buffer MUST remain readable through a non-numeric guard byte - pass a
NUL-terminated string (`fio_bstr` qualifies) or append a guard byte.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 multipart.h`
3 public symbols.
### Types
#### `fio_multipart_parser_callbacks_s`
```c
typedef struct {
/**
* Called for each regular form field (no filename).
* Returns user-defined context (can be NULL).
*
* Note: If on_field_start is provided, this callback is ignored and the
* streaming field callbacks (on_field_start/on_field_data/on_field_end)
* are used instead.
*/
void *(*on_field)(void *udata,
fio_buf_info_s name,
fio_buf_info_s value,
fio_buf_info_s content_type);
/**
* Called when a large field starts (optional).
* If NULL but on_field exists, on_field is used instead (backward
* compatible). Returns context for this field (passed to
* on_field_data/on_field_end).
*/
void *(*on_field_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s content_type);
/**
* Called with field data chunk.
* May be called multiple times per field for streaming.
* Returns non-zero to abort parsing.
*/
int (*on_field_data)(void *udata, void *field_ctx, fio_buf_info_s data);
/**
* Called when field ends.
*/
void (*on_field_end)(void *udata, void *field_ctx);
/**
* Called when a file upload starts.
* Returns context for this file (passed to on_file_data/on_file_end).
*/
void *(*on_file_start)(void *udata,
fio_buf_info_s name,
fio_buf_info_s filename,
fio_buf_info_s content_type);
/**
* Called with file data chunk.
* May be called multiple times per file for streaming.
* Returns non-zero to abort parsing.
*/
int (*on_file_data)(void *udata, void *file_ctx, fio_buf_info_s data);
/**
* Called when file upload ends.
*/
void (*on_file_end)(void *udata, void *file_ctx);
/**
* Called on parse error (optional).
*/
void (*on_error)(void *udata);
} fio_multipart_parser_callbacks_s
```
The MIME multipart parser callbacks.
_Symbol type:_ `type`
#### `fio_multipart_result_s`
```c
typedef struct {
/** Number of bytes consumed from the input buffer. */
size_t consumed;
/** Number of form fields parsed. */
size_t field_count;
/** Number of files parsed. */
size_t file_count;
/** Error code: 0 = success, -1 = error, -2 = need more data. */
int err;
} fio_multipart_result_s
```
The MIME multipart parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_multipart_parse`
```c
fio_multipart_result_s fio_multipart_parse(const fio_multipart_parser_callbacks_s *callbacks, void *udata, fio_buf_info_s boundary, const char *data, size_t len)
```
Parse MIME multipart data.
`callbacks` contains the callback functions (should be static const).
`udata` is user data passed to all callbacks.
`boundary` is the multipart boundary string (without leading "--").
`data` is the data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `consumed`: Number of bytes consumed from the buffer
- `field_count`: Number of form fields parsed
- `file_count`: Number of files parsed
- `err`: 0 = success, -1 = error, -2 = need more data
For streaming, call again with remaining data appended to unconsumed data.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 resp3.h`
24 public symbols.
### Macros
#### `FIO_RESP3_MAX_NESTING`
```c
#define FIO_RESP3_MAX_NESTING 32
```
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_THRESHOLD`
```c
#define FIO_RESP3_STREAM_THRESHOLD 4096
```
_Symbol type:_ `macro`
#### `FIO_RESP3_SIMPLE_STR`
```c
#define FIO_RESP3_SIMPLE_STR '+'
```
Simple String: `+\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_SIMPLE_ERR`
```c
#define FIO_RESP3_SIMPLE_ERR '-'
```
Simple Error: `-\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_NUMBER`
```c
#define FIO_RESP3_NUMBER ':'
```
Number: `:\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_NULL`
```c
#define FIO_RESP3_NULL '_'
```
Null: `_\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_DOUBLE`
```c
#define FIO_RESP3_DOUBLE ','
```
Double: `,\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BOOL`
```c
#define FIO_RESP3_BOOL '#'
```
Boolean: `#t\r\n` or `#f\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BIGNUM`
```c
#define FIO_RESP3_BIGNUM '('
```
Big Number: `(\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BLOB_STR`
```c
#define FIO_RESP3_BLOB_STR '$'
```
Blob String: `$\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_BLOB_ERR`
```c
#define FIO_RESP3_BLOB_ERR '!'
```
Blob Error: `!\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_VERBATIM`
```c
#define FIO_RESP3_VERBATIM '='
```
Verbatim String: `=\r\n\r\n`
_Symbol type:_ `macro`
#### `FIO_RESP3_ARRAY`
```c
#define FIO_RESP3_ARRAY '*'
```
Array: `*\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_MAP`
```c
#define FIO_RESP3_MAP '%'
```
Map: `%\r\n...key-value pairs...`
_Symbol type:_ `macro`
#### `FIO_RESP3_SET`
```c
#define FIO_RESP3_SET '~'
```
Set: `~\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_PUSH`
```c
#define FIO_RESP3_PUSH '>'
```
Push: `>\r\n...elements...`
_Symbol type:_ `macro`
#### `FIO_RESP3_ATTR`
```c
#define FIO_RESP3_ATTR '|'
```
Attribute: `|\r\n...key-value pairs...`
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_CHUNK`
```c
#define FIO_RESP3_STREAM_CHUNK ';'
```
Streamed string chunk: `;`
_Symbol type:_ `macro`
#### `FIO_RESP3_STREAM_END`
```c
#define FIO_RESP3_STREAM_END '.'
```
Streamed aggregate end: `.`
_Symbol type:_ `macro`
### Types
#### `fio_resp3_frame_s`
```c
typedef struct {
/** Context for this container (returned by on_array/on_map/etc) */
void *ctx;
/** For maps: the pending key waiting for its value */
void *key;
/** Expected remaining elements */
int64_t remaining;
/** Type of this frame */
uint8_t type;
/** Is this a streaming type? */
uint8_t streaming;
/** For maps: are we expecting a key (0) or value (1)? */
uint8_t expecting_value;
/** Set treated as map: need to duplicate values as key+value */
uint8_t set_as_map;
} fio_resp3_frame_s
```
Parser frame for tracking nested structures
_Symbol type:_ `type`
#### `fio_resp3_parser_s`
```c
typedef struct {
/** User data passed to all callbacks */
void *udata;
/** Current nesting depth */
uint32_t depth;
/** Protocol error flag */
uint8_t error;
/** Streaming string in progress flag */
uint8_t streaming_string;
/** Streaming string type (FIO_RESP3_BLOB_STR, FIO_RESP3_BLOB_ERR, etc.) */
uint8_t streaming_string_type;
/** Streamed fixed-length blob: trailing CRLF bytes pending (0...2) */
uint8_t streaming_blob_crlf;
/** Context for streaming string (from on_start_string) */
void *streaming_string_ctx;
/**
* Streamed fixed-length blob: data bytes remaining to stream.
* Zero when inactive or in `$?` (chunked) streaming mode.
*/
int64_t streaming_remaining;
/** Stack for nested structures */
fio_resp3_frame_s stack[FIO_RESP3_MAX_NESTING];
} fio_resp3_parser_s
```
RESP3 parser state
_Symbol type:_ `type`
#### `fio_resp3_callbacks_s`
```c
typedef struct {
/* ===== Primitive Callbacks - return the created object ===== */
/** Called when NULL (`_`) is received. Returns new object. */
void *(*on_null)(void *udata);
/** Called when Boolean (`#t` or `#f`) is received. Returns new object. */
void *(*on_bool)(void *udata, int is_true);
/** Called when a Number (`:`) is parsed. Returns new object. */
void *(*on_number)(void *udata, int64_t num);
/** Called when a Double (`,`) is parsed. Returns new object. */
void *(*on_double)(void *udata, double num);
/** Called when a Big Number (`(`) is parsed. Returns new object. */
void *(*on_bignum)(void *udata, const void *data, size_t len);
/**
* Called when a complete String is received.
* `type` is FIO_RESP3_SIMPLE_STR, FIO_RESP3_BLOB_STR, or FIO_RESP3_VERBATIM.
* Returns new object.
*/
void *(*on_string)(void *udata, const void *data, size_t len, uint8_t type);
/**
* Called when an error message is received (simple `-` or blob `!`).
* `type` is FIO_RESP3_SIMPLE_ERR or FIO_RESP3_BLOB_ERR.
* Returns new object.
*/
void *(*on_error)(void *udata, const void *data, size_t len, uint8_t type);
/* ===== Container Callbacks - receive parent ctx, return new ctx ===== */
/** Called when an Array starts. Returns new array context. */
void *(*on_array)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Map starts. Returns new map context. */
void *(*on_map)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Set starts. Returns new set context. */
void *(*on_set)(void *udata, void *parent_ctx, int64_t len);
/** Called when a Push message starts. Returns new push context. */
void *(*on_push)(void *udata, void *parent_ctx, int64_t len);
/** Called when an Attribute starts. Returns new attribute context. */
void *(*on_attr)(void *udata, void *parent_ctx, int64_t len);
/* ===== Push Callbacks - add child to container ===== */
/** Add value to array. Returns non-zero on error. */
int (*array_push)(void *udata, void *ctx, void *value);
/** Add key-value pair to map. Returns non-zero on error. */
int (*map_push)(void *udata, void *ctx, void *key, void *value);
/** Add value to set. Returns non-zero on error. */
int (*set_push)(void *udata, void *ctx, void *value);
/** Add value to push message. Returns non-zero on error. */
int (*push_push)(void *udata, void *ctx, void *value);
/** Add key-value pair to attribute. Returns non-zero on error. */
int (*attr_push)(void *udata, void *ctx, void *key, void *value);
/* ===== Done Callbacks (optional) - finalize container ===== */
/** Called when array is complete. Returns final object. */
void *(*array_done)(void *udata, void *ctx);
/** Called when map is complete. Returns final object. */
void *(*map_done)(void *udata, void *ctx);
/** Called when set is complete. Returns final object. */
void *(*set_done)(void *udata, void *ctx);
/** Called when push is complete. Returns final object. */
void *(*push_done)(void *udata, void *ctx);
/** Called when attribute is complete. Returns final object. */
void *(*attr_done)(void *udata, void *ctx);
/* ===== Error Handling ===== */
/** Free an unused object (e.g., orphaned key on error). */
void (*free_unused)(void *udata, void *obj);
/** Called on protocol error. */
void *(*on_error_protocol)(void *udata);
/* ===== Streaming String Callbacks (optional) ===== */
/**
* Called when a blob string starts (before data arrives).
* `len` is the declared length of the string ((size_t)-1 for streaming).
* `type` is FIO_RESP3_BLOB_STR, FIO_RESP3_BLOB_ERR, or FIO_RESP3_VERBATIM.
* Returns a context for the string being built (e.g., a string buffer).
* If NULL is returned, falls back to buffering and calling on_string when
* complete.
*/
void *(*on_start_string)(void *udata, size_t len, uint8_t type);
/**
* Called with partial string data (may be called multiple times).
* `ctx` is the context returned by on_start_string.
* Returns 0 on success, non-zero to abort parsing.
*/
int (*on_string_write)(void *udata, void *ctx, const void *data, size_t len);
/**
* Called when the string is complete.
* `ctx` is the context returned by on_start_string.
* Returns the final string object to be used as a value.
*/
void *(*on_string_done)(void *udata, void *ctx, uint8_t type);
} fio_resp3_callbacks_s
```
The RESP3 parser callbacks (designed to be static const).
All callbacks receive `udata` from the parser state as their first argument.
_Symbol type:_ `type`
#### `fio_resp3_result_s`
```c
typedef struct {
/** The parsed top-level object (or NULL on error/incomplete) */
void *obj;
/** Number of bytes consumed from the buffer */
size_t consumed;
/** Non-zero if an error occurred */
int err;
} fio_resp3_result_s
```
The RESP3 parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_resp3_parse`
```c
fio_resp3_result_s fio_resp3_parse(fio_resp3_parser_s *parser, const fio_resp3_callbacks_s *callbacks, const void *buf, size_t len)
```
Parse RESP3 data from buffer.
`parser` is the parser state. Initialize with `{.udata = my_data}` for first
call. For continuation after partial parse, pass the same parser.
`callbacks` contains the callback functions (should be static const).
`buf` is the data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `obj`: The parsed top-level object (NULL if incomplete or error)
- `consumed`: Number of bytes consumed from the buffer
- `err`: Non-zero if a protocol error occurred
For partial data, the parser state is preserved. Call again with remaining
data appended to unconsumed data.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 sock.h`
35 public symbols.
### Macros
#### `FIO_SOCKET_INVALID`
```c
#define FIO_SOCKET_INVALID INVALID_SOCKET
```
Sentinel value for an invalid socket handle.
_Symbol type:_ `macro`
#### `FIO_SOCK_FD_ISVALID`
```c
#define FIO_SOCK_FD_ISVALID(fd) ((fio_socket_i)(fd) != FIO_SOCKET_INVALID)
```
_Symbol type:_ `macro`
#### `FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT`
```c
#define FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT (1ULL << 24)
```
_Symbol type:_ `macro`
#### `FIO_SOCK_UNIX`
```c
#define FIO_SOCK_UNIX 0
```
_Symbol type:_ `macro`
#### `FIO_SOCK_UNIX_PRIVATE`
```c
#define FIO_SOCK_UNIX_PRIVATE 0
```
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_RW`
```c
#define FIO_SOCK_WAIT_RW(fd, timeout_) \
fio_sock_wait_io(fd, POLLIN | POLLOUT, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_R`
```c
#define FIO_SOCK_WAIT_R(fd, timeout_) fio_sock_wait_io(fd, POLLIN, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_WAIT_W`
```c
#define FIO_SOCK_WAIT_W(fd, timeout_) fio_sock_wait_io(fd, POLLOUT, timeout_)
```
A helper macro that waits on a single IO with no callbacks (0 = no event)
_Symbol type:_ `macro`
#### `FIO_SOCK_IS_OPEN`
```c
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, (POLLOUT | POLLRDHUP), 0) & \
(POLLRDHUP | POLLHUP | POLLNVAL)))
```
A helper macro that tests if a socket was closed.
_Symbol type:_ `macro`
### Types
#### `fio_socket_i`
```c
typedef int fio_socket_i
```
Native socket handle type: int on POSIX.
_Symbol type:_ `type`
#### `fio_sock_open_flags_e`
```c
typedef enum {
FIO_SOCK_SERVER = 0,
FIO_SOCK_CLIENT = 1,
FIO_SOCK_NONBLOCK = 2,
FIO_SOCK_TCP = 4,
FIO_SOCK_UDP = 8,
#ifdef AF_UNIX
FIO_SOCK_UNIX = 16,
FIO_SOCK_UNIX_PRIVATE = (16 | 32),
#else
#define FIO_SOCK_UNIX 0
#define FIO_SOCK_UNIX_PRIVATE 0
#endif
} fio_sock_open_flags_e
```
Socket type flags
_Symbol type:_ `type`
### Functions
#### `fio_sock_write`
```c
inline ssize_t fio_sock_write(fio_socket_i fd, const void *buf, size_t len)
```
Acts as POSIX write. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_read`
```c
inline ssize_t fio_sock_read(fio_socket_i fd, void *buf, size_t len)
```
Acts as POSIX read. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_sendto`
```c
inline ssize_t fio_sock_sendto(fio_socket_i fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen)
```
Acts as POSIX sendto. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_recvfrom`
```c
inline ssize_t fio_sock_recvfrom(fio_socket_i fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen)
```
Acts as POSIX recvfrom. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_dup`
```c
inline fio_socket_i fio_sock_dup(fio_socket_i fd)
```
Acts as POSIX dup. Sets O_CLOEXEC on the new fd.
_Symbol type:_ `function`
#### `fio_sock_close`
```c
inline int fio_sock_close(fio_socket_i fd)
```
Acts as POSIX close. Use this function for portability with WinSock2.
_Symbol type:_ `function`
#### `fio_sock_accept`
```c
#define fio_sock_accept(fd, addr, addrlen) accept(fd, addr, addrlen)
```
Acts as POSIX accept. Use this macro for portability with WinSock2.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sock_bind`
```c
inline int fio_sock_bind(fio_socket_i fd, const struct sockaddr *addr, socklen_t addrlen)
```
Portable bind. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_connect`
```c
inline int fio_sock_connect(fio_socket_i fd, const struct sockaddr *addr, socklen_t addrlen)
```
Portable connect. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_listen`
```c
inline int fio_sock_listen(fio_socket_i fd, int backlog)
```
Portable listen. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_setsockopt`
```c
inline int fio_sock_setsockopt(fio_socket_i fd, int level, int optname, const void *optval, socklen_t optlen)
```
Portable setsockopt. POSIX version.
_Symbol type:_ `function`
#### `fio_sock_socketpair`
```c
inline int fio_sock_socketpair(fio_socket_i fds[2])
```
Creates a connected socket pair using POSIX socketpair().
_Symbol type:_ `function`
#### `fio_sock_pipe`
```c
inline int fio_sock_pipe(fio_socket_i fds[2])
```
Creates a POSIX pipe. fds[0] = read end, fds[1] = write end.
On Windows, fio_sock_pipe() is defined in the FIO_OS_WIN block above and
delegates to fio_sock_socketpair() (loopback TCP).
_Symbol type:_ `function`
#### `fio_sock_open`
```c
inline fio_socket_i fio_sock_open(const char *restrict address, const char *restrict port, uint16_t flags)
```
Creates a new socket according to the provided flags.
The `port` string will be ignored when `FIO_SOCK_UNIX` is set.
_Symbol type:_ `function`
#### `fio_sock_open2`
```c
fio_socket_i fio_sock_open2(const char *url, uint16_t flags)
```
Creates a new socket, according to the provided flags.
_Symbol type:_ `function`
#### `fio_sock_address_new`
```c
inline struct addrinfo *fio_sock_address_new(const char *restrict address, const char *restrict port, int sock_type)
```
Attempts to resolve an address to a valid IP6 / IP4 address pointer.
The `sock_type` element should be a socket type, such as `SOCK_DGRAM` (UDP)
or `SOCK_STREAM` (TCP/IP).
The address should be freed using `fio_sock_address_free`.
_Symbol type:_ `function`
#### `fio_sock_address_free`
```c
inline void fio_sock_address_free(struct addrinfo *a)
```
Frees the pointer returned by `fio_sock_address_new`.
_Symbol type:_ `function`
#### `fio_sock_peer_addr`
```c
fio_buf_info_s fio_sock_peer_addr(fio_socket_i s)
```
Returns a human readable address representation of the socket's peer address.
On error, returns a NULL buffer with zero length.
Buffer lengths are limited to 63 bytes.
This function is limited in its thread safety to 128 threads / calls.
_Symbol type:_ `function`
#### `fio_sock_open_local`
```c
fio_socket_i fio_sock_open_local(struct addrinfo *addr, int nonblock)
```
Creates a new network socket and binds it to a local address.
_Symbol type:_ `function`
#### `fio_sock_open_remote`
```c
fio_socket_i fio_sock_open_remote(struct addrinfo *addr, int nonblock)
```
Creates a new network socket and connects it to a remote address.
_Symbol type:_ `function`
#### `fio_sock_open_unix`
```c
fio_socket_i fio_sock_open_unix(const char *address, uint16_t flags)
```
Creates a new Unix socket and binds it to a local address.
_Symbol type:_ `function`
#### `fio_sock_set_non_block`
```c
int fio_sock_set_non_block(fio_socket_i fd)
```
Sets a file descriptor / socket to non blocking state.
_Symbol type:_ `function`
#### `fio_sock_maximize_limits`
```c
size_t fio_sock_maximize_limits(size_t maximum_limit)
```
Attempts to maximize the allowed open file limits. returns known limit
_Symbol type:_ `function`
#### `fio_sock_wait_io`
```c
short fio_sock_wait_io(fio_socket_i fd, short events, int timeout)
```
Returns 0 on timeout, -1 on error or the events that are valid.
A zero timeout returns immediately.
Possible events include POLLIN | POLLOUT
Possible return values include POLLIN | POLLOUT | POLLHUP | POLLNVAL
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 state callbacks.h`
4 public symbols.
### Types
#### `fio_state_event_type_e`
```c
typedef enum {
/** Called once during library initialization. */
FIO_CALL_ON_INITIALIZE,
/** Called once before starting up the IO reactor. */
FIO_CALL_PRE_START,
/** Called before forking starts for a worker group. */
FIO_CALL_BEFORE_FORK,
/** Called by a child worker process right after it was forked. */
FIO_CALL_IN_CHILD,
/** Called by the master process right after spawning a worker. */
FIO_CALL_IN_MASTER,
/** Called after forking ends for a worker group. */
FIO_CALL_AFTER_FORK,
/** Called by each worker thread in a Server Async queue as it starts. */
FIO_CALL_ON_WORKER_THREAD_START,
/** Called every time a *Worker* process starts. */
FIO_CALL_ON_START,
/** Reserved for internal use. */
FIO_CALL_RESERVED1,
/** Reserved for internal use. */
FIO_CALL_RESERVED2,
/** User state event queue (unused, available for the user). */
FIO_CALL_ON_USER1,
/** User state event queue (unused, available for the user). */
FIO_CALL_ON_USER2,
/** Called when facil.io enters idling mode. */
FIO_CALL_ON_IDLE,
/** A reversed user state event queue (unused, available for the user). */
FIO_CALL_ON_USER1_REVERSE,
/** A reversed user state event queue (unused, available for the user). */
FIO_CALL_ON_USER2_REVERSE,
/** Reserved for internal use. */
FIO_CALL_RESERVED1_REVERSED,
/** Reserved for internal use. */
FIO_CALL_RESERVED2_REVERSED,
/** Called before starting the shutdown sequence. */
FIO_CALL_ON_SHUTDOWN,
/** Called by each worker the moment it detects the master process crashed. */
FIO_CALL_ON_PARENT_CRUSH,
/** Called by the parent (master) after a worker process crashed. */
FIO_CALL_ON_CHILD_CRUSH,
/** Called by each worker thread in a Server Async queue as it ends. */
FIO_CALL_ON_WORKER_THREAD_END,
/** Called when wither a *Worker* or *Master* stopped. */
FIO_CALL_ON_STOP,
/** An alternative to the system's at_exit. */
FIO_CALL_AT_EXIT,
/** Use after cleanup (e.g., leak testing). */
FIO_CALL_AFTER_EXIT,
/** used for testing and array allocation - must be last. */
FIO_CALL_NEVER
} fio_state_event_type_e
```
a callback type enum
_Symbol type:_ `type`
### Functions
#### `fio_state_callback_add`
```c
void fio_state_callback_add(fio_state_event_type_e, void (*func)(void *), void *arg)
```
Adds a callback to the list of callbacks to be called for the event.
_Symbol type:_ `function`
#### `fio_state_callback_remove`
```c
int fio_state_callback_remove(fio_state_event_type_e, void (*func)(void *), void *arg)
```
Removes a callback from the list of callbacks to be called for the event.
_Symbol type:_ `function`
#### `fio_state_callback_force`
```c
void fio_state_callback_force(fio_state_event_type_e)
```
Forces all the existing callbacks to run, as if the event occurred.
Callbacks for all initialization / idling tasks are called in order of
creation (where fio_state_event_type_e <= FIO_CALL_ON_IDLE).
Callbacks for all cleanup oriented tasks are called in reverse order of
creation (where fio_state_event_type_e >= FIO_CALL_ON_SHUTDOWN).
During an event, changes to the callback list are ignored (callbacks can't
add or remove other callbacks for the same event).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 time.h`
17 public symbols.
### Functions
#### `fio_time_real`
```c
inline struct timespec fio_time_real(void)
```
Returns human (watch) time... this value isn't as safe for measurements.
_Symbol type:_ `function`
#### `fio_time_mono`
```c
inline struct timespec fio_time_mono(void)
```
Returns monotonic time.
_Symbol type:_ `function`
#### `fio_time_nano`
```c
inline int64_t fio_time_nano(void)
```
Returns monotonic time in nano-seconds (now in 1 billionth of a second).
_Symbol type:_ `function`
#### `fio_time_micro`
```c
inline int64_t fio_time_micro(void)
```
Returns monotonic time in micro-seconds (now in 1 millionth of a second).
_Symbol type:_ `function`
#### `fio_time_milli`
```c
inline int64_t fio_time_milli(void)
```
Returns monotonic time in milliseconds.
_Symbol type:_ `function`
#### `fio_time2milli`
```c
inline int64_t fio_time2milli(struct timespec)
```
Converts a `struct timespec` to milliseconds.
_Symbol type:_ `function`
#### `fio_time2micro`
```c
inline int64_t fio_time2micro(struct timespec)
```
Converts a `struct timespec` to microseconds.
_Symbol type:_ `function`
#### `fio_time2gm`
```c
struct tm fio_time2gm(time_t time)
```
A faster (yet less localized) alternative to `gmtime_r`.
See the libc `gmtime_r` documentation for details.
Falls back to `gmtime_r` for dates before epoch.
_Symbol type:_ `function`
#### `fio_gm2time`
```c
time_t fio_gm2time(struct tm tm)
```
Converts a `struct tm` to time in seconds (assuming UTC).
_Symbol type:_ `function`
#### `fio_time2rfc7231`
```c
size_t fio_time2rfc7231(char *target, time_t time)
```
Writes an RFC 7231 date representation (HTTP date format) to target.
Usually requires 29 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2rfc2109`
```c
size_t fio_time2rfc2109(char *target, time_t time)
```
Writes an RFC 2109 date representation to target (HTTP Cookie Format).
Usually requires 31 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2rfc2822`
```c
size_t fio_time2rfc2822(char *target, time_t time)
```
Writes an RFC 2822 date representation to target (Internet Message Format).
Usually requires 28 to 29 characters, although this may vary.
_Symbol type:_ `function`
#### `fio_time2log`
```c
size_t fio_time2log(char *target, time_t time)
```
Writes a date representation to target in common log format. i.e.,
[DD/MMM/yyyy:hh:mm:ss +0000]
Usually requires 29 characters (including square brackets and NUL).
_Symbol type:_ `function`
#### `fio_time2iso`
```c
size_t fio_time2iso(char *target, time_t time)
```
Writes a date representation to target in ISO 8601 format. i.e.,
YYYY-MM-DD HH:MM:SS
Usually requires 20 characters (including NUL).
_Symbol type:_ `function`
#### `fio_time_add`
```c
inline struct timespec fio_time_add(struct timespec t, struct timespec t2)
```
Adds two `struct timespec` objects.
_Symbol type:_ `function`
#### `fio_time_add_milli`
```c
inline struct timespec fio_time_add_milli(struct timespec t, int64_t milli)
```
Adds milliseconds to a `struct timespec` object.
_Symbol type:_ `function`
#### `fio_time_cmp`
```c
inline int fio_time_cmp(struct timespec t1, struct timespec t2)
```
Compares two `struct timespec` objects.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 urlencoded.h`
3 public symbols.
### Types
#### `fio_url_encoded_parser_callbacks_s`
```c
typedef struct {
/**
* Called for each name=value pair found.
*
* `name` and `value` point directly into the original input data.
* The data is NOT decoded - caller must decode if needed.
*
* Returns the (possibly updated) udata, or NULL to stop parsing.
*/
void *(*on_pair)(void *udata, fio_buf_info_s name, fio_buf_info_s value);
/**
* Called on parsing error (optional).
*
* Currently not used since URL-encoded parsing is very permissive,
* but reserved for future use.
*/
void (*on_error)(void *udata);
} fio_url_encoded_parser_callbacks_s
```
The URL-encoded parser callbacks.
Callbacks receive `udata` as their first argument.
_Symbol type:_ `type`
#### `fio_url_encoded_result_s`
```c
typedef struct {
/** Number of bytes consumed from the buffer. */
size_t consumed;
/** Number of name=value pairs found. */
size_t count;
/** Non-zero if an error occurred (callback returned NULL). */
int err;
} fio_url_encoded_result_s
```
The URL-encoded parse result type.
_Symbol type:_ `type`
### Functions
#### `fio_url_encoded_parse`
```c
fio_url_encoded_result_s fio_url_encoded_parse(const fio_url_encoded_parser_callbacks_s *callbacks, void *udata, const char *data, size_t len)
```
Parse URL-encoded data from buffer.
`callbacks` contains the callback functions (should be static const).
`udata` is user data passed to callbacks.
`data` is the URL-encoded data to parse.
`len` is the length of the data.
Returns a result struct containing:
- `consumed`: Number of bytes consumed from the buffer
- `count`: Number of name=value pairs found
- `err`: Non-zero if parsing was stopped (callback returned NULL)
Parsing rules:
- Pairs are separated by `&`
- Name and value are separated by `=`
- Empty value is valid: `name=` → value.len = 0
- Missing `=` means value is empty: `name` → name="name", value.len = 0
- Empty name with value: `=value` → name.len = 0, value="value"
- Empty pairs (`&&`) are skipped
Note: The parser does NOT decode percent-encoded characters.
Use `fio_string_write_url_dec` to decode if needed.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/004 websocket parser.h`
29 public symbols.
### Macros
#### `FIO_WEBSOCKET_DEFAULT_MAX_FRAME`
```c
#define FIO_WEBSOCKET_DEFAULT_MAX_FRAME (1ULL << 30)
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_PARSE_ERROR`
```c
#define FIO_WEBSOCKET_PARSE_ERROR ((size_t)-1)
```
Parse error sentinel.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV1`
```c
#define FIO_WEBSOCKET_RSV1 0x4U /* byte-0 bit 6 (permessage-deflate) */
```
RSV bit constants for the send-side API. These are the 3-bit values shifted
into byte-0 bits 4..6 on the wire (0x4 → RSV1=0x40, 0x2 → RSV2=0x20, 0x1 →
RSV3=0x10).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV2`
```c
#define FIO_WEBSOCKET_RSV2 0x2U /* byte-0 bit 5 */
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_RSV3`
```c
#define FIO_WEBSOCKET_RSV3 0x1U /* byte-0 bit 4 */
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_CONT`
```c
#define FIO_WEBSOCKET_OP_CONT 0x0
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_TEXT`
```c
#define FIO_WEBSOCKET_OP_TEXT 0x1
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_BINARY`
```c
#define FIO_WEBSOCKET_OP_BINARY 0x2
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_CLOSE`
```c
#define FIO_WEBSOCKET_OP_CLOSE 0x8
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_PING`
```c
#define FIO_WEBSOCKET_OP_PING 0x9
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_OP_PONG`
```c
#define FIO_WEBSOCKET_OP_PONG 0xA
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_HEADER`
```c
#define FIO_WEBSOCKET_STATE_HEADER 0
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_PAYLOAD`
```c
#define FIO_WEBSOCKET_STATE_PAYLOAD 1
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_CLOSED`
```c
#define FIO_WEBSOCKET_STATE_CLOSED 0xFE
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATE_ERROR`
```c
#define FIO_WEBSOCKET_STATE_ERROR 0xFF
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_FIN`
```c
#define FIO_WEBSOCKET_FLAG_FIN 0x80
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_MASKED`
```c
#define FIO_WEBSOCKET_FLAG_MASKED 0x40
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_OPCODE_MASK`
```c
#define FIO_WEBSOCKET_FLAG_OPCODE_MASK 0x3C
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_OPCODE_SHIFT`
```c
#define FIO_WEBSOCKET_FLAG_OPCODE_SHIFT 2
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK`
```c
#define FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK 0x03
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_PAUSED`
```c
#define FIO_WEBSOCKET_FLAG2_PAUSED 0x80
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK`
```c
#define FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK 0x70
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT`
```c
#define FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT 4
```
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_FIN`
```c
#define FIO_WEBSOCKET_GET_FIN(p) (((p)->flags & FIO_WEBSOCKET_FLAG_FIN) != 0)
```
Read the FIN bit from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MASKED`
```c
#define FIO_WEBSOCKET_GET_MASKED(p) \
(((p)->flags & FIO_WEBSOCKET_FLAG_MASKED) != 0)
```
Read the MASK bit from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_OPCODE`
```c
#define FIO_WEBSOCKET_GET_OPCODE(p) \
(((p)->flags & FIO_WEBSOCKET_FLAG_OPCODE_MASK) >> \
FIO_WEBSOCKET_FLAG_OPCODE_SHIFT)
```
Read the opcode from the current frame.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MSG_OPCODE`
```c
#define FIO_WEBSOCKET_GET_MSG_OPCODE(p) \
((p)->flags & FIO_WEBSOCKET_FLAG_MSG_OPCODE_MASK)
```
Read the message opcode (1=text, 2=binary, 0=none open).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_PAUSED`
```c
#define FIO_WEBSOCKET_GET_PAUSED(p) \
(((p)->flags2 & FIO_WEBSOCKET_FLAG2_PAUSED) != 0)
```
Read the paused flag (one-message-per-parse gate).
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_GET_MSG_RSV`
```c
#define FIO_WEBSOCKET_GET_MSG_RSV(p) \
(((p)->flags2 & FIO_WEBSOCKET_FLAG2_MSG_RSV_MASK) >> \
FIO_WEBSOCKET_FLAG2_MSG_RSV_SHIFT)
```
Read the RSV bits from the opening frame (3-bit format).
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/005 cli.h`
23 public symbols.
### Macros
#### `FIO_CLI_ARG_NONE`
```c
#define FIO_CLI_ARG_NONE FIO_CLI_ARG_PRINT_HEADER
```
_Symbol type:_ `macro`
#### `FIO_CLI_STRING`
```c
#define FIO_CLI_STRING(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_STRING, .l = line})
```
Indicates the CLI argument should be a String (default).
_Symbol type:_ `macro`
#### `FIO_CLI_BOOL`
```c
#define FIO_CLI_BOOL(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_BOOL, .l = line})
```
Indicates the CLI argument is a Boolean value.
_Symbol type:_ `macro`
#### `FIO_CLI_INT`
```c
#define FIO_CLI_INT(line) ((fio___cli_line_s){.t = FIO_CLI_ARG_INT, .l = line})
```
Indicates the CLI argument should be an Integer (numerical).
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT`
```c
#define FIO_CLI_PRINT(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT, .l = line})
```
Indicates the CLI string should be printed as is with proper offset.
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT_LINE`
```c
#define FIO_CLI_PRINT_LINE(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT_LINE, .l = line})
```
Indicates the CLI string should be printed as is with no offset.
_Symbol type:_ `macro`
#### `FIO_CLI_PRINT_HEADER`
```c
#define FIO_CLI_PRINT_HEADER(line) \
((fio___cli_line_s){.t = FIO_CLI_ARG_PRINT_HEADER, .l = line})
```
Indicates the CLI string should be printed as a header.
_Symbol type:_ `macro`
### Types
#### `fio_cli_arg_e`
```c
typedef enum {
/** A String CLI argument */
FIO_CLI_ARG_STRING,
/** A Boolean CLI argument */
FIO_CLI_ARG_BOOL,
/** An integer CLI argument */
FIO_CLI_ARG_INT,
FIO_CLI_ARG_PRINT,
FIO_CLI_ARG_PRINT_LINE,
FIO_CLI_ARG_PRINT_HEADER,
} fio_cli_arg_e
```
Used internally.
_Symbol type:_ `type`
### Functions
#### `fio_cli_start`
```c
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
fio_cli_start((argc), \
(argv), \
(unnamed_min), \
(unnamed_max), \
(description), \
(fio___cli_line_s[]){__VA_ARGS__, {0}})
```
This function parses the Command Line Interface (CLI), creating a temporary
"dictionary" that allows easy access to the CLI using their names or aliases.
Command line arguments may be typed. If an optional type requirement is
provided and the provided arument fails to match the required type, execution
will end and an error message will be printed along with a short "help".
The function / macro accepts the following arguments:
- `argc`: command line argument count.
- `argv`: command line argument list (array).
- `unnamed_min`: the required minimum of un-named arguments.
- `unnamed_max`: the maximum limit of un-named arguments.
- `description`: a C string containing the program's description.
- named arguments list: a list of C strings describing named arguments.
The following optional type requirements are:
* FIO_CLI_STRING(desc_line) - (default) string argument.
* FIO_CLI_BOOL(desc_line) - boolean argument (no value).
* FIO_CLI_INT(desc_line) - integer argument.
* FIO_CLI_PRINT_HEADER(desc_line) - extra header for output.
* FIO_CLI_PRINT(desc_line) - extra information for output.
Argument names MUST start with the '-' character. The first word starting
without the '-' character will begin the description for the CLI argument.
The arguments "-?", "-h", "-help" and "--help" are automatically handled
unless overridden.
Un-named arguments shouldn't be listed in the named arguments list.
Example use:
fio_cli_start(argc, argv, 0, 0, "The NAME example accepts the following:",
FIO_CLI_PRINT_HREADER("Concurrency:"),
FIO_CLI_INT("-t -thread number of threads to run."),
FIO_CLI_INT("-w -workers number of workers to run."),
FIO_CLI_PRINT_HREADER("Address Binding:"),
"-b, -address the address to bind to.",
FIO_CLI_INT("-p,-port the port to bind to."),
FIO_CLI_PRINT("\t\tset port to zero (0) for Unix s."),
FIO_CLI_PRINT_HREADER("Logging:"),
FIO_CLI_BOOL("-v -log enable logging.")
);
This would allow access to the named arguments:
fio_cli_get("-b") == fio_cli_get("-address");
Once all the data was accessed, free the parsed data dictionary using:
fio_cli_end();
It should be noted, arguments will be recognized in a number of forms, i.e.:
app -t=1 -p3000 -a localhost
This function is NOT thread safe.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_cli_start`
```c
void fio_cli_start FIO_NOOP(int argc, char const *argv[], int unnamed_min, int unnamed_max, char const *description, fio___cli_line_s *arguments)
```
Never use the function directly, always use the MACRO, because the macro
attaches a NULL marker at the end of the `names` argument collection.
_Symbol type:_ `function`
#### `fio_cli_end`
```c
void fio_cli_end(void)
```
Clears the memory used by the CLI dictionary, removing all parsed data.
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_get`
```c
char const *fio_cli_get(char const *name)
```
Returns the argument's value as a NUL terminated C String.
_Symbol type:_ `function`
#### `fio_cli_get_str`
```c
fio_buf_info_s fio_cli_get_str(char const *name)
```
Returns the argument's value as a NUL terminated `fio_buf_info_s`.
_Symbol type:_ `function`
#### `fio_cli_get_i`
```c
int64_t fio_cli_get_i(char const *name)
```
Returns the argument's value as an integer.
_Symbol type:_ `function`
#### `fio_cli_get_bool`
```c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
```
This MACRO returns the argument's value as a boolean.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_cli_unnamed_count`
```c
unsigned int fio_cli_unnamed_count(void)
```
Returns the number of unnamed argument.
_Symbol type:_ `function`
#### `fio_cli_unnamed`
```c
char const *fio_cli_unnamed(unsigned int index)
```
Returns the unnamed argument using a 0 based `index`.
_Symbol type:_ `function`
#### `fio_cli_unnamed_str`
```c
fio_buf_info_s fio_cli_unnamed_str(unsigned int index)
```
Returns the unnamed argument using a 0 based `index`.
_Symbol type:_ `function`
#### `fio_cli_set`
```c
void fio_cli_set(char const *name, char const *value)
```
Sets the argument's value as a NUL terminated C String.
fio_cli_set("-p", "hello");
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_set_i`
```c
void fio_cli_set_i(char const *name, int64_t i)
```
Sets the argument's value as a NUL terminated C String.
fio_cli_start(argc, argv,
"this is example accepts the following options:",
"-p -port the port to bind to", FIO_CLI_INT;
fio_cli_set("-p", "hello"); // fio_cli_get("-p") == fio_cli_get("-port");
This function is NOT thread safe.
_Symbol type:_ `function`
#### `fio_cli_set_unnamed`
```c
unsigned int fio_cli_set_unnamed(unsigned int index, const char *)
```
Sets / adds an unnamed argument to the 0 based array of unnamed elements.
_Symbol type:_ `function`
#### `fio_cli_each`
```c
size_t fio_cli_each(int (*task)(fio_buf_info_s name, fio_buf_info_s value, fio_cli_arg_e arg_type, void *udata), void *udata)
```
Calls `task` for every argument received.
_Symbol type:_ `function`
#### `fio_cli_type`
```c
fio_cli_arg_e fio_cli_type(char const *name)
```
Returns the argument's expected content type.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/010 mem.h`
50 public symbols.
### Macros
#### `FIO_MEMORY_ALIGN_LOG`
```c
#define FIO_MEMORY_ALIGN_LOG 6
```
Allocation alignment, MUST be >= 3 and <= 10
_Symbol type:_ `macro`
#### `FIO_MEMORY_ALIGN_SIZE`
```c
#define FIO_MEMORY_ALIGN_SIZE (1UL << (FIO_MEMORY_ALIGN_LOG))
```
The minimal allocation size & alignment.
_Symbol type:_ `macro`
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG 21
```
The logarithmic size of a single allocation "chunk" (16 blocks).
Limited to >=17 and <=24.
By default 21, which is a ~2Mb allocation per system call, resulting in a
maximum allocation size of 64Kb.
_Symbol type:_ `macro`
#### `FIO_MEMORY_CACHE_SLOTS`
```c
#define FIO_MEMORY_CACHE_SLOTS 4
```
The number of system allocation "chunks" to cache even if they are not in
use.
_Symbol type:_ `macro`
#### `FIO_MEMORY_INITIALIZE_ALLOCATIONS`
```c
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS \
FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT
```
Forces the allocator to zero out memory early and often, so allocations
return initialized memory (bytes are all zeros).
This will make the realloc2 safe for use (all data not copied is zero).
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG 2
```
The number of blocks per system allocation.
More blocks protect against fragmentation, but lower the maximum number that
can be allocated without reverting to mmap.
Range: 0-4
Recommended: depends on object allocation sizes, usually 1 or 2.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ENABLE_BIG_ALLOC`
```c
#define FIO_MEMORY_ENABLE_BIG_ALLOC 1
```
Uses a whole system allocation to support bigger allocations.
Could increase fragmentation costs.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT`
```c
#define FIO_MEMORY_ARENA_COUNT -1
```
Memory arenas mitigate thread contention while using more memory.
Note that at some point arenas are statistically irrelevant... except when
benchmarking contention in multi-core machines.
Negative values will result in dynamic selection based on CPU core count.
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT_FALLBACK`
```c
#define FIO_MEMORY_ARENA_COUNT_FALLBACK 24
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_ARENA_COUNT_MAX`
```c
#define FIO_MEMORY_ARENA_COUNT_MAX 64
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_USE_THREAD_MUTEX`
```c
#define FIO_MEMORY_USE_THREAD_MUTEX 1
```
If arena count isn't linked to the CPU count, threads might busy-spin.
It is better to slow wait than fast busy spin when the work in the lock is
longer... and system allocations are performed inside arena locks.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCKS_PER_ALLOCATION`
```c
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION \
(1UL << FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG)
```
the number of allocation blocks per system allocation.
_Symbol type:_ `macro`
#### `FIO_MEMORY_SYS_ALLOCATION_SIZE`
```c
#define FIO_MEMORY_SYS_ALLOCATION_SIZE \
(1UL << FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG)
```
the total number of bytes consumed per system allocation.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BLOCK_ALLOC_LIMIT`
```c
#define FIO_MEMORY_BLOCK_ALLOC_LIMIT \
(FIO_MEMORY_SYS_ALLOCATION_SIZE >> (FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG + 2))
```
The maximum allocation size, after which a big/system allocation is used.
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_ALLOC_LIMIT`
```c
#define FIO_MEMORY_BIG_ALLOC_LIMIT \
(FIO_MEMORY_SYS_ALLOCATION_SIZE >> \
(FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG > 3 \
? 3 \
: FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG))
```
the limit of a big allocation, if enabled
_Symbol type:_ `macro`
#### `FIO_MEMORY_ALLOC_LIMIT`
```c
#define FIO_MEMORY_ALLOC_LIMIT FIO_MEMORY_BIG_ALLOC_LIMIT
```
_Symbol type:_ `macro`
#### `FIO_MEM_BYTES2PAGES`
```c
#define FIO_MEM_BYTES2PAGES(size) \
((size > (SIZE_MAX - ((1UL << FIO_MEM_PAGE_SIZE_LOG) - 1))) \
? size \
: (((size_t)(size) + ((1UL << FIO_MEM_PAGE_SIZE_LOG) - 1)) & \
((~(size_t)0) << FIO_MEM_PAGE_SIZE_LOG)))
```
_Symbol type:_ `macro`
#### `FIO_PAGE_ALIGN`
```c
#define FIO_PAGE_ALIGN \
__attribute__((assume_aligned((1UL << FIO_MEM_PAGE_SIZE_LOG))))
```
_Symbol type:_ `macro`
#### `FIO_PAGE_ALIGN_NEW`
```c
#define FIO_PAGE_ALIGN_NEW \
__attribute__((malloc, assume_aligned((1UL << FIO_MEM_PAGE_SIZE_LOG))))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_HEADER_SIZE`
```c
#define FIO_MEMORY_HEADER_SIZE \
((sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_chunk_s)) + \
(FIO_MEMORY_ALIGN_SIZE - 1)) & \
(~(FIO_MEMORY_ALIGN_SIZE - 1)))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_UNITS_PER_BLOCK`
```c
#define FIO_MEMORY_UNITS_PER_BLOCK \
(FIO_MEMORY_BLOCK_SIZE / FIO_MEMORY_ALIGN_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_STATE_SIZE`
```c
#define FIO_MEMORY_STATE_SIZE(arena_count) \
FIO_MEM_BYTES2PAGES( \
(sizeof(*FIO_NAME(FIO_MEMORY_NAME, __mem_state)) + \
(sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_arena_s)) * (arena_count))))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_MARKER`
```c
#define FIO_MEMORY_BIG_BLOCK_MARKER ((~(uint32_t)0) << 2)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_HEADER_SIZE`
```c
#define FIO_MEMORY_BIG_BLOCK_HEADER_SIZE \
(((sizeof(FIO_NAME(FIO_MEMORY_NAME, __mem_big_block_s)) + \
((FIO_MEMORY_ALIGN_SIZE - 1))) & \
((~(0UL)) << FIO_MEMORY_ALIGN_LOG)))
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_BIG_BLOCK_SIZE`
```c
#define FIO_MEMORY_BIG_BLOCK_SIZE \
(FIO_MEMORY_SYS_ALLOCATION_SIZE - FIO_MEMORY_BIG_BLOCK_HEADER_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_MEMORY_UNITS_PER_BIG_BLOCK`
```c
#define FIO_MEMORY_UNITS_PER_BIG_BLOCK \
(FIO_MEMORY_BIG_BLOCK_SIZE / FIO_MEMORY_ALIGN_SIZE)
```
_Symbol type:_ `macro`
#### `FIO_TEST_MULTI_THREADED`
```c
#define FIO_TEST_MULTI_THREADED 0
```
_Symbol type:_ `macro`
### Functions
#### `fio_malloc`
```c
void *FIO_MEM_ALIGN_NEW fio_malloc(size_t size)
```
Allocates memory using a per-CPU core block memory pool.
Memory is zeroed out.
Allocations above FIO_MEMORY_BLOCK_ALLOC_LIMIT will be redirected to `mmap`,
as if `mempool_mmap` was called.
`mempool_malloc` promises a best attempt at providing locality between
consecutive calls, but locality can't be guaranteed.
_Symbol type:_ `function`
#### `fio_calloc`
```c
void *FIO_MEM_ALIGN_NEW fio_calloc(size_t size_per_unit, size_t unit_count)
```
same as calling `fio_malloc(size_per_unit * unit_count)`;
Allocations above FIO_MEMORY_BLOCK_ALLOC_LIMIT will be redirected to `mmap`,
as if `mempool_mmap` was called.
_Symbol type:_ `function`
#### `fio_free`
```c
void fio_free(void *ptr)
```
Frees memory that was allocated using this library.
_Symbol type:_ `function`
#### `fio_realloc`
```c
void *FIO_MEM_ALIGN fio_realloc(void *ptr, size_t new_size)
```
Re-allocates memory. An attempt to avoid copying the data is made only for
big memory allocations (larger than FIO_MEMORY_BLOCK_ALLOC_LIMIT).
_Symbol type:_ `function`
#### `fio_realloc2`
```c
void *FIO_MEM_ALIGN fio_realloc2(void *ptr, size_t new_size, size_t copy_len)
```
Re-allocates memory. An attempt to avoid copying the data is made only for
big memory allocations (larger than FIO_MEMORY_BLOCK_ALLOC_LIMIT).
This variation can perform better, as it might copy less data.
_Symbol type:_ `function`
#### `fio_realloc_aligned`
```c
void *FIO_MEM_ALIGN fio_realloc_aligned(void *ptr, size_t new_size, size_t copy_len, size_t alignment)
```
Re-allocates memory, enforcing a minimum pointer alignment.
This is the core of the aligned allocation API: `malloc_aligned` and
`calloc_aligned` simply route to this function with a NULL `ptr`.
The `alignment` argument is normalized as follows:
- `0` is treated as the allocator's default (`FIO_MEMORY_ALIGN_SIZE`);
- non power-of-2 values are rounded DOWN to the nearest power of 2;
- the effective alignment is the maximum of the requested alignment, the
current alignment of `ptr` (if any) and `FIO_MEMORY_ALIGN_SIZE`;
- effective values above `FIO_MEMORY_SYS_ALLOCATION_SIZE` fail
(returns NULL, sets `errno` to `EINVAL`).
Data preservation semantics are identical to `realloc2` (`copy_len` bytes).
In-place growth may only occur when the existing pointer already satisfies
the requested alignment.
_Symbol type:_ `function`
#### `fio_malloc_aligned`
```c
void *FIO_MEM_ALIGN_NEW fio_malloc_aligned(size_t size, size_t alignment)
```
Allocates `size` bytes, returning a pointer aligned to (at least)
`alignment`. Same semantics as `realloc_aligned(NULL, size, 0, alignment)`.
_Symbol type:_ `function`
#### `fio_calloc_aligned`
```c
void *FIO_MEM_ALIGN_NEW fio_calloc_aligned(size_t size_per_unit, size_t unit_count, size_t alignment)
```
Same as `malloc_aligned(size_per_unit * unit_count, alignment)`,
except that the allocated memory is zeroed out.
_Symbol type:_ `function`
#### `fio_mmap`
```c
void *FIO_MEM_ALIGN_NEW fio_mmap(size_t size)
```
Allocates memory directly using `mmap`, this is preferred for objects that
both require almost a page of memory (or more) and expect a long lifetime.
However, since this allocation will invoke the system call (`mmap`), it will
be inherently slower.
`mempoll_free` can be used for deallocating the memory.
_Symbol type:_ `function`
#### `fio_malloc_after_fork`
```c
void fio_malloc_after_fork(void)
```
When forking is called manually, call this function to reset the facil.io
memory allocator's locks.
This is provided in case of forking in a multi-threaded environment, which
some people do even though it's bad.
_Symbol type:_ `function`
#### `fio_malloc_arenas`
```c
size_t fio_malloc_arenas(void)
```
Arena count for the allocator.
_Symbol type:_ `function`
#### `fio_malloc_block_size`
```c
size_t fio_malloc_block_size(void)
```
_Symbol type:_ `function`
#### `fio_malloc_print_state`
```c
void fio_malloc_print_state(void)
```
Prints the allocator's data structure. May be used for debugging.
_Symbol type:_ `function`
#### `fio_malloc_print_free_block_list`
```c
void fio_malloc_print_free_block_list(void)
```
Prints the allocator's free block list. May be used for debugging.
_Symbol type:_ `function`
#### `fio_malloc_print_settings`
```c
void fio_malloc_print_settings(void)
```
Prints the settings used to define the allocator.
_Symbol type:_ `function`
#### `fio_malloc_sys_alloc_size`
```c
inline size_t fio_malloc_sys_alloc_size(void)
```
System allocation sizes (bytes per system allocation).
_Symbol type:_ `function`
#### `fio_malloc_cache_slots`
```c
inline size_t fio_malloc_cache_slots(void)
```
Cached system allocations (free, but held on to).
_Symbol type:_ `function`
#### `fio_malloc_alignment`
```c
inline size_t fio_malloc_alignment(void)
```
Allocations alignment.
_Symbol type:_ `function`
#### `fio_malloc_alignment_log`
```c
inline size_t fio_malloc_alignment_log(void)
```
Allocations alignment log (base 2).
_Symbol type:_ `function`
#### `fio_malloc_alloc_limit`
```c
inline size_t fio_malloc_alloc_limit(void)
```
Allocation limit (at which point do we switch to system allocations?).
_Symbol type:_ `function`
#### `fio_malloc_arena_alloc_limit`
```c
inline size_t fio_malloc_arena_alloc_limit(void)
```
Allocation limit for arena based allocation (vs. big allocations).
_Symbol type:_ `function`
#### `fio_realloc_is_safe`
```c
inline size_t fio_realloc_is_safe(void)
```
_Symbol type:_ `function`
#### `fio_alloc_size`
```c
inline size_t fio_alloc_size(size_t minimum_bytes)
```
Returns the number of usable bytes the allocator will actually reserve for
an allocation request of `minimum_bytes` (the allocation's size class).
The result is alignment agnostic - alignment padding, if any, is reserved
in addition to the returned value.
NOTE: when the custom allocator is bypassed (`FIO_MEMORY_DISABLE`), the
system allocator exposes no rounding guarantee and `minimum_bytes` is
returned unchanged.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/011 string core.h`
103 public symbols.
### Macros
#### `FIO_STRING_WRITE_STR1`
```c
#define FIO_STRING_WRITE_STR1(str_) \
((fio_string_write_s){ \
.klass = 1, \
.info.str = {.len = (size_t)FIO_STRLEN((str_)), .buf = (str_)}})
```
A macro to add a String to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_STR2`
```c
#define FIO_STRING_WRITE_STR2(str_, len_) \
((fio_string_write_s){.klass = 1, .info.str = {.len = (len_), .buf = (str_)}})
```
A macro to add a String with known length to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_STR_INFO`
```c
#define FIO_STRING_WRITE_STR_INFO(str_) \
((fio_string_write_s){.klass = 1, \
.info.str = {.len = (str_).len, .buf = (str_).buf}})
```
A macro to add a String with known length to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_NUM`
```c
#define FIO_STRING_WRITE_NUM(num) \
((fio_string_write_s){.klass = 2, .info.i = (int64_t)(num)})
```
A macro to add a signed number to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_UNUM`
```c
#define FIO_STRING_WRITE_UNUM(num) \
((fio_string_write_s){.klass = 3, .info.u = (uint64_t)(num)})
```
A macro to add an unsigned number to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_HEX`
```c
#define FIO_STRING_WRITE_HEX(num) \
((fio_string_write_s){.klass = 4, .info.u = (uint64_t)(num)})
```
A macro to add a hex representation to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_BIN`
```c
#define FIO_STRING_WRITE_BIN(num) \
((fio_string_write_s){.klass = 5, .info.u = (uint64_t)(num)})
```
A macro to add a binary representation to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_WRITE_FLOAT`
```c
#define FIO_STRING_WRITE_FLOAT(num) \
((fio_string_write_s){.klass = 6, .info.f = (double)(num)})
```
A macro to add a float (double) to `fio_string_write2`.
_Symbol type:_ `macro`
#### `FIO_STRING_SYS_REALLOC`
```c
#define FIO_STRING_SYS_REALLOC fio_string_sys_reallocate
```
Default reallocation callback implementation using libc `realloc`.
_Symbol type:_ `macro`
#### `FIO_STRING_REALLOC`
```c
#define FIO_STRING_REALLOC fio_string_default_reallocate
```
Default reallocation callback implementation using the default allocator
_Symbol type:_ `macro`
#### `FIO_STRING_ALLOC_COPY`
```c
#define FIO_STRING_ALLOC_COPY fio_string_default_allocate_copy
```
Default reallocation callback for memory that mustn't be freed.
_Symbol type:_ `macro`
#### `FIO_STRING_ALLOC_KEY`
```c
#define FIO_STRING_ALLOC_KEY fio_string_default_key_alloc
```
default allocator for the fio_keystr_s string data..
_Symbol type:_ `macro`
#### `FIO_STRING_FREE`
```c
#define FIO_STRING_FREE fio_string_default_free
```
Frees memory that was allocated with the default callbacks.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE2`
```c
#define FIO_STRING_FREE2 fio_string_default_free2
```
Frees memory that was allocated with the default callbacks.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_KEY`
```c
#define FIO_STRING_FREE_KEY fio_string_default_free_key
```
Frees memory that was allocated for a key string.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_NOOP`
```c
#define FIO_STRING_FREE_NOOP fio_string_default_free_noop
```
Does nothing.
_Symbol type:_ `macro`
#### `FIO_STRING_FREE_NOOP2`
```c
#define FIO_STRING_FREE_NOOP2 fio_string_default_free_noop2
```
Does nothing.
_Symbol type:_ `macro`
#### `FIO_KEYSTR_CONST`
```c
#define FIO_KEYSTR_CONST ((size_t)-1LL)
```
_Symbol type:_ `macro`
### Types
#### `fio_string_realloc_fn`
```c
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len)
```
A reallocation callback type for buffers in a `fio_str_info_s`.
The callback MUST allocate at least `len + 1` bytes, setting the new capacity
in `dest->capa`.
Returns 0 on success, -1 on error.
_Symbol type:_ `type`
#### `fio_string_write_s`
```c
typedef struct {
size_t klass;
union {
struct {
size_t len;
const char *buf;
} str;
double f;
int64_t i;
uint64_t u;
} info;
} fio_string_write_s
```
Argument type used by fio_string_write2.
_Symbol type:_ `type`
#### `fio_keystr_s`
```c
struct fio_keystr_s
```
a semi-opaque type used for the `fio_keystr` functions
_Symbol type:_ `type`
### Functions
#### `fio_string_write`
```c
FIO_SFUNC int fio_string_write(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *restrict src, size_t len)
```
Writes data to the end of the string in the `fio_string_s` struct,
returning an updated `fio_string_s` struct.
The returned string is NUL terminated if edited.
* `dest` an `fio_string_s` struct containing the destination string.
* `reallocate` is a callback that attempts to reallocate more memory (i.e.,
using `realloc`) and returns an updated `fio_string_s` struct containing the
updated capacity and buffer pointer (as well as the original length).
On failure the original `fio_string_s` should be returned. if
`reallocate` is NULL or fails, the data copied will be truncated.
* `src` is the data to be written to the end of `dest`.
* `len` is the length of the data to be written to the end of `dest`.
Note: this function performs only minimal checks and assumes that `dest` is
fully valid - i.e., that `dest.capa >= dest.len`, that `dest.buf` is
valid, etc'.
An example for a `reallocate` callback using the system's `realloc` function:
int fio_string_realloc_system(fio_str_info_s *dest, size_t len_no_nul) {
const size_t new_capa = fio_string_capa4len(len_no_nul);
void *tmp = realloc(dest.buf, new_capa);
if (!tmp)
return -1;
dest.capa = new_capa;
dest.buf = (char *)tmp;
return 0;
}
An example for using the function:
void example(void) {
char buf[32];
fio_str_info_s str = FIO_STR_INFO3(buf, 0, 32);
fio_string_write(&str, NULL, "The answer is: 0x", 17);
str.len += fio_ltoa(str.buf + str.len, 42, 16);
fio_string_write(&str, NULL, "!\n", 2);
printf("%s", str.buf);
}
_Symbol type:_ `function`
#### `fio_string_replace`
```c
int fio_string_replace(fio_str_info_s *dest, fio_string_realloc_fn reallocate, intptr_t start_pos, size_t overwrite_len, const void *src, size_t len)
```
Similar to `fio_string_write`, only replacing/inserting a sub-string in a
specific location.
Negative `start_pos` values are calculated backwards, `-1` == end of String.
When `overwrite_len` is zero, the function will insert the data at
`start_pos`, pushing existing data until after the inserted data.
If `overwrite_len` is non-zero, than `overwrite_len` bytes will be
overwritten (or deleted).
If `len == 0` than `src` will be ignored and the data marked for replacement
will be erased.
_Symbol type:_ `function`
#### `fio_string_write2`
```c
int fio_string_write2(fio_str_info_s *restrict dest, fio_string_realloc_fn reallocate, const fio_string_write_s srcs[])
```
Writes a group of objects (strings, numbers, etc') to `dest`.
`dest` and `reallocate` are similar to `fio_string_write`.
`src` is an array of `fio_string_write_s` structs, ending with a struct
that's all set to 0.
Use the `fio_string_write2` macro for ease, i.e.:
fio_str_info_s str = {0};
fio_string_write2(&str, my_reallocate,
FIO_STRING_WRITE_STR1("The answer is: "),
FIO_STRING_WRITE_NUM(42),
FIO_STRING_WRITE_STR2("(0x", 3),
FIO_STRING_WRITE_HEX(42),
FIO_STRING_WRITE_STR2(")", 1));
Note: this function might end up allocating more memory than absolutely
required as it favors fast performance over memory savings. It performs only
a single allocation (if any) and computes numeral string length only when
writing the numbers to the string.
_Symbol type:_ `function`
#### `fio_string_write2`
```c
#define fio_string_write2(dest, reallocate, ...) \
fio_string_write2((dest), \
(reallocate), \
(fio_string_write_s[]){__VA_ARGS__, {0}})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_string_write_i`
```c
int fio_string_write_i(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_u`
```c
int fio_string_write_u(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_hex`
```c
int fio_string_write_hex(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_write_bin`
```c
int fio_string_write_bin(fio_str_info_s *dest, fio_string_realloc_fn reallocate, uint64_t i)
```
_Symbol type:_ `function`
#### `fio_string_printf`
```c
FIO___PRINTF_STYLE(3, 0) int fio_string_printf( fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *format, ...)
```
Similar to fio_string_write, only using printf semantics.
_Symbol type:_ `function`
#### `fio_string_vprintf`
```c
FIO___PRINTF_STYLE(3, 0) int fio_string_vprintf( fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *format, va_list argv)
```
Similar to fio_string_write, only using vprintf semantics.
_Symbol type:_ `function`
#### `fio_string_write_escape`
```c
int fio_string_write_escape(fio_str_info_s *restrict dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data at the end of the String, escaping the data using JSON semantics.
The JSON semantic are common to many programming languages, promising a UTF-8
String while making it easy to read and copy the string during debugging.
_Symbol type:_ `function`
#### `fio_string_write_unescape`
```c
int fio_string_write_unescape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *enscaped, size_t enscaped_len)
```
Writes an escaped data into the string after un-escaping the data.
_Symbol type:_ `function`
#### `fio_string_write_base32enc`
```c
int fio_string_write_base32enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data to String using base64 encoding.
_Symbol type:_ `function`
#### `fio_string_write_base32dec`
```c
int fio_string_write_base32dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to String.
_Symbol type:_ `function`
#### `fio_string_write_base64enc`
```c
int fio_string_write_base64enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len, uint8_t url_encoded)
```
Writes data to String using base64 encoding.
_Symbol type:_ `function`
#### `fio_string_write_base64dec`
```c
int fio_string_write_base64dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to String.
_Symbol type:_ `function`
#### `fio_string_write_url_enc`
```c
int fio_string_write_url_enc(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes data to String using URL encoding (a.k.a., percent encoding).
_Symbol type:_ `function`
#### `fio_string_write_url_dec`
```c
int fio_string_write_url_dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded URL data to String, decoding + to spaces.
_Symbol type:_ `function`
#### `fio_string_write_path_dec`
```c
int fio_string_write_path_dec(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *encoded, size_t encoded_len)
```
Writes decoded URL data to String, without decoding + to spaces.
_Symbol type:_ `function`
#### `fio_string_write_html_escape`
```c
int fio_string_write_html_escape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *raw, size_t raw_len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fio_string_write_html_unescape`
```c
int fio_string_write_html_unescape(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const void *enscaped, size_t enscaped_len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fio_string_readfd`
```c
int fio_string_readfd(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int fd, intptr_t start_at, size_t limit)
```
Writes up to `limit` bytes from `fd` into `dest`, starting at `start_at`.
If `limit` is 0 (or less than 0) data will be written until EOF.
If `start_at` is negative, position will be calculated from the end of the
file where `-1 == EOF`.
Note: this will fail unless used on actual files (not sockets, not pipes).
_Symbol type:_ `function`
#### `fio_string_readfile`
```c
int fio_string_readfile(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *filename, intptr_t start_at, size_t limit)
```
Opens the file `filename` and pastes it's contents (or a slice ot it) at
the end of the String. If `limit == 0`, than the data will be read until
EOF.
If the file can't be located, opened or read, or if `start_at` is beyond
the EOF position, NULL is returned in the state's `data` field.
_Symbol type:_ `function`
#### `fio_string_getdelim_fd`
```c
int fio_string_getdelim_fd(fio_str_info_s *dest, fio_string_realloc_fn reallocate, int fd, intptr_t start_at, char delim, size_t limit)
```
Writes up to `limit` bytes from `fd` into `dest`, starting at `start_at` and
ending either at the first occurrence of `delim` or at EOF.
If `limit` is 0 (or less than 0) as much data as may be required will be
written.
If `start_at` is negative, position will be calculated from the end of the
file where `-1 == EOF`.
Note: this will fail unless used on actual seekable files (not sockets, not
pipes).
_Symbol type:_ `function`
#### `fio_string_getdelim_file`
```c
int fio_string_getdelim_file(fio_str_info_s *dest, fio_string_realloc_fn reallocate, const char *filename, intptr_t start_at, char delim, size_t limit)
```
Opens the file `filename`, calls `fio_string_getdelim_fd` and closes the
file.
_Symbol type:_ `function`
#### `fio_string_capa4len`
```c
inline size_t fio_string_capa4len(size_t new_len)
```
_Symbol type:_ `function`
#### `fio_string_default_reallocate`
```c
int fio_string_default_reallocate(fio_str_info_s *dst, size_t len)
```
default reallocation callback implementation.
_Symbol type:_ `function`
#### `fio_string_default_allocate_copy`
```c
int fio_string_default_allocate_copy(fio_str_info_s *dest, size_t new_capa)
```
default reallocation callback for memory that mustn't be freed.
_Symbol type:_ `function`
#### `fio_string_default_free`
```c
void fio_string_default_free(void *)
```
frees memory that was allocated with the default callbacks.
_Symbol type:_ `function`
#### `fio_string_default_free2`
```c
void fio_string_default_free2(fio_str_info_s str)
```
frees memory that was allocated with the default callbacks.
_Symbol type:_ `function`
#### `fio_string_default_free_noop`
```c
void fio_string_default_free_noop(void *)
```
does nothing.
_Symbol type:_ `function`
#### `fio_string_default_free_noop2`
```c
void fio_string_default_free_noop2(fio_str_info_s str)
```
does nothing.
_Symbol type:_ `function`
#### `fio_string_default_key_alloc`
```c
void *fio_string_default_key_alloc(size_t len)
```
default allocator for the fio_keystr_s string data..
_Symbol type:_ `function`
#### `fio_string_default_free_key`
```c
void fio_string_default_free_key(void *, size_t)
```
frees a fio_keystr_s memory that was allocated with the default callback.
_Symbol type:_ `function`
#### `fio_string_utf8_valid`
```c
bool fio_string_utf8_valid(fio_str_info_s str)
```
Returns 1 if the String is UTF-8 valid and 0 if not.
_Symbol type:_ `function`
#### `fio_string_utf8_len`
```c
size_t fio_string_utf8_len(fio_str_info_s str)
```
Returns the String's length in UTF-8 characters or 0 if invalid.
_Symbol type:_ `function`
#### `fio_string_utf8_valid_code_point`
```c
size_t fio_string_utf8_valid_code_point(const void *u8c, size_t buf_len)
```
Returns 0 if non-UTF-8 or returns 1-4 (UTF-8 if a valid char).
_Symbol type:_ `function`
#### `fio_string_utf8_select`
```c
int fio_string_utf8_select(fio_str_info_s str, intptr_t *pos, size_t *len)
```
Takes a UTF-8 character selection information (UTF-8 position and length)
and updates the same variables so they reference the raw byte slice
information.
If the String isn't UTF-8 valid up to the requested selection, than `pos`
will be updated to `-1` otherwise values are always positive.
The returned `len` value may be shorter than the original if there wasn't
enough data left to accommodate the requested length. When a `len` value of
`0` is returned, this means that `pos` marks the end of the String.
Returns -1 on error and 0 on success.
_Symbol type:_ `function`
#### `fio_string_is_greater_buf`
```c
int fio_string_is_greater_buf(fio_buf_info_s a, fio_buf_info_s b)
```
Compares two `fio_buf_info_s`, returning 1 if data in a is bigger than b.
Note: returns 0 if data in b is bigger than or equal(!).
_Symbol type:_ `function`
#### `fio_string_is_greater`
```c
inline int fio_string_is_greater(fio_str_info_s a, fio_str_info_s b)
```
Compares two strings, returning 1 if string a is bigger than string b.
Note: returns 0 if string b is bigger than string a or if strings are equal.
_Symbol type:_ `function`
#### `fio_bstr_reserve`
```c
inline char *fio_bstr_reserve(char *bstr, size_t len)
```
Reserves `len` for future `write` operations (used to minimize realloc).
_Symbol type:_ `function`
#### `fio_bstr_copy`
```c
inline char *fio_bstr_copy(char *bstr)
```
Copies a `fio_bstr` using "copy on write".
_Symbol type:_ `function`
#### `fio_bstr_free`
```c
inline void fio_bstr_free(char *bstr)
```
Frees a binary string allocated by a `fio_bstr` function. Returns NULL.
_Symbol type:_ `function`
#### `fio_bstr_info`
```c
inline fio_str_info_s fio_bstr_info(const char *bstr)
```
Returns information about the fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_buf`
```c
inline fio_buf_info_s fio_bstr_buf(const char *bstr)
```
Returns information about the fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_len`
```c
inline size_t fio_bstr_len(const char *bstr)
```
Gets the length of the fio_bstr. `bstr` MUST NOT be NULL.
_Symbol type:_ `function`
#### `fio_bstr_len_set`
```c
inline char *fio_bstr_len_set(char *bstr, size_t len)
```
Sets the length of the fio_bstr. `bstr` MUST NOT be NULL.
_Symbol type:_ `function`
#### `fio_bstr_is_greater`
```c
FIO_SFUNC int fio_bstr_is_greater(const char *a, const char *b)
```
Compares to see if fio_bstr a is greater than fio_bstr b (for FIO_SORT).
_Symbol type:_ `function`
#### `fio_bstr_is_eq`
```c
FIO_SFUNC int fio_bstr_is_eq(const char *a, const char *b)
```
Compares to see if fio_bstr a is equal to another fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_is_eq2info`
```c
FIO_SFUNC int fio_bstr_is_eq2info(const char *a_, fio_str_info_s b)
```
Compares to see if fio_bstr a is equal to another String.
_Symbol type:_ `function`
#### `fio_bstr_is_eq2buf`
```c
FIO_SFUNC int fio_bstr_is_eq2buf(const char *a_, fio_buf_info_s b)
```
Compares to see if fio_bstr a is equal to another String.
_Symbol type:_ `function`
#### `fio_bstr_write`
```c
inline char *fio_bstr_write(char *bstr, const void *restrict src, size_t len)
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
Returns existing string on reallocation error (true for all fio_bstr_write).
_Symbol type:_ `function`
#### `fio_bstr_replace`
```c
inline char *fio_bstr_replace(char *bstr, intptr_t start_pos, size_t overwrite_len, const void *src, size_t len)
```
Replaces data in a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write2`
```c
inline char *fio_bstr_write2(char *bstr, const fio_string_write_s srcs[])
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write2`
```c
#define fio_bstr_write2(bstr, ...) \
fio_bstr_write2(bstr, (fio_string_write_s[]){__VA_ARGS__, {0}})
```
Writes data to a fio_bstr, returning the address of the new fio_bstr.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_bstr_write_i`
```c
inline char *fio_bstr_write_i(char *bstr, int64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_u`
```c
inline char *fio_bstr_write_u(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_hex`
```c
inline char *fio_bstr_write_hex(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_bin`
```c
inline char *fio_bstr_write_bin(char *bstr, uint64_t num)
```
Writes number to a fio_bstr, returning the address of the new fio_bstr.
_Symbol type:_ `function`
#### `fio_bstr_write_escape`
```c
inline char *fio_bstr_write_escape(char *bstr, const void *src, size_t len)
```
Writes escaped data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_unescape`
```c
inline char *fio_bstr_write_unescape(char *bstr, const void *src, size_t len)
```
Un-escapes and writes data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_base64enc`
```c
inline char *fio_bstr_write_base64enc(char *bstr, const void *src, size_t len, uint8_t url_encoded)
```
Writes base64 encoded data to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_base64dec`
```c
inline char *fio_bstr_write_base64dec(char *bstr, const void *src, size_t len)
```
Decodes base64 data and writes to a fio_bstr, returning its new address.
_Symbol type:_ `function`
#### `fio_bstr_write_url_enc`
```c
inline char *fio_bstr_write_url_enc(char *bstr, const void *data, size_t len)
```
Writes data to String using URL encoding (a.k.a., percent encoding).
_Symbol type:_ `function`
#### `fio_bstr_write_url_dec`
```c
inline char *fio_bstr_write_url_dec(char *bstr, const void *encoded, size_t len)
```
Writes decoded URL data to String.
_Symbol type:_ `function`
#### `fio_bstr_write_html_escape`
```c
inline char *fio_bstr_write_html_escape(char *bstr, const void *raw, size_t len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fio_bstr_write_html_unescape`
```c
inline char *fio_bstr_write_html_unescape(char *bstr, const void *escaped, size_t len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fio_bstr_readfd`
```c
inline char *fio_bstr_readfd(char *bstr, int fd, intptr_t start_at, intptr_t limit)
```
Writes to the String from a regular file `fd`.
_Symbol type:_ `function`
#### `fio_bstr_readfile`
```c
inline char *fio_bstr_readfile(char *bstr, const char *filename, intptr_t start_at, intptr_t limit)
```
Writes to the String from a regular file named `filename`.
_Symbol type:_ `function`
#### `fio_bstr_getdelim_file`
```c
inline char *fio_bstr_getdelim_file(char *bstr, const char *filename, intptr_t start_at, char delim, size_t limit)
```
Writes to the String from a regular file named `filename`.
_Symbol type:_ `function`
#### `fio_bstr_getdelim_fd`
```c
inline char *fio_bstr_getdelim_fd(char *bstr, int fd, intptr_t start_at, char delim, size_t limit)
```
Writes to the String from a regular file `fd`.
_Symbol type:_ `function`
#### `fio_bstr_printf`
```c
inline FIO___PRINTF_STYLE(2, 0) char *fio_bstr_printf(char *bstr, const char *format, ...)
```
Writes a `fio_bstr` in `printf` style.
_Symbol type:_ `function`
#### `fio_bstr_reallocate`
```c
int fio_bstr_reallocate(fio_str_info_s *dest, size_t len)
```
default reallocation callback implementation - mostly for internal use.
_Symbol type:_ `function`
#### `fio_keystr_buf`
```c
inline fio_buf_info_s fio_keystr_buf(fio_keystr_s *str)
```
returns the Key String. NOTE: Key Strings are NOT NUL TERMINATED!
_Symbol type:_ `function`
#### `fio_keystr_info`
```c
inline fio_str_info_s fio_keystr_info(fio_keystr_s *str)
```
returns the Key String. NOTE: Key Strings are NOT NUL TERMINATED!
_Symbol type:_ `function`
#### `fio_keystr_tmp`
```c
inline fio_keystr_s fio_keystr_tmp(const char *buf, uint32_t len)
```
Returns a TEMPORARY `fio_keystr_s`.
_Symbol type:_ `function`
#### `fio_keystr_init`
```c
FIO_SFUNC fio_keystr_s fio_keystr_init(fio_str_info_s str, void *(*alloc_func)(size_t len))
```
Returns an initialized `fio_keystr_s` containing a copy of `str`.
_Symbol type:_ `function`
#### `fio_keystr_destroy`
```c
FIO_SFUNC void fio_keystr_destroy(fio_keystr_s *key, void (*free_func)(void *, size_t))
```
Destroys an initialized `fio_keystr_s`.
_Symbol type:_ `function`
#### `fio_keystr_is_eq`
```c
inline int fio_keystr_is_eq(fio_keystr_s a, fio_keystr_s b)
```
Compares two Key Strings.
_Symbol type:_ `function`
#### `fio_keystr_is_eq2`
```c
inline int fio_keystr_is_eq2(fio_keystr_s a_, fio_str_info_s b)
```
Compares a Key String to any String - used internally by the hash map.
_Symbol type:_ `function`
#### `fio_keystr_is_eq3`
```c
inline int fio_keystr_is_eq3(fio_keystr_s a_, fio_buf_info_s b)
```
Compares a Key String to any String - used internally by the hash map.
_Symbol type:_ `function`
#### `fio_keystr_hash`
```c
inline uint64_t fio_keystr_hash(fio_keystr_s a)
```
Returns a good-enough `fio_keystr_s` risky hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/012 gfm.h`
10 public symbols.
### Macros
#### `FIO_GFM_MAX_DEPTH`
```c
#define FIO_GFM_MAX_DEPTH 255
```
Maximum container nesting depth (blockquote + list).
_Symbol type:_ `macro`
#### `FIO_GFM_MAX_TABLE_COLUMNS`
```c
#define FIO_GFM_MAX_TABLE_COLUMNS 64
```
Maximum table columns (2 bits each → 4 columns per byte).
_Symbol type:_ `macro`
#### `FIO_GFM_REF_CACHE_SIZE`
```c
#define FIO_GFM_REF_CACHE_SIZE 128
```
Maximum reference definitions held in the fast cache.
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_GENERIC`
```c
#define FIO_GFM_ERR_GENERIC -1
```
Error codes — negative values are parser-generated.
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_DEPTH`
```c
#define FIO_GFM_ERR_DEPTH -2
```
_Symbol type:_ `macro`
#### `FIO_GFM_ERR_INPUT`
```c
#define FIO_GFM_ERR_INPUT -3
```
_Symbol type:_ `macro`
#### `FIO_GFM_F_TIGHT`
```c
#define FIO_GFM_F_TIGHT ((uint8_t)1U << 0)
```
List is tight (no blank lines between items).
_Symbol type:_ `macro`
#### `FIO_GFM_F_LOOSE_SEEN`
```c
#define FIO_GFM_F_LOOSE_SEEN ((uint8_t)1U << 1)
```
A blank line was seen between items — list is loose.
_Symbol type:_ `macro`
#### `FIO_GFM_F_TASK`
```c
#define FIO_GFM_F_TASK ((uint8_t)1U << 2)
```
GFM task-list marker present.
_Symbol type:_ `macro`
#### `FIO_GFM_F_TASK_CHECKED`
```c
#define FIO_GFM_F_TASK_CHECKED ((uint8_t)1U << 3)
```
GFM task-list marker is checked.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/102 poll api.h`
16 public symbols.
### Macros
#### `FIO_POLL_POSSIBLE_FLAGS`
```c
#define FIO_POLL_POSSIBLE_FLAGS (POLLIN | POLLOUT | POLLPRI)
```
The user flags IO events recognize
_Symbol type:_ `macro`
#### `FIO_POLL_MAX_EVENTS`
```c
#define FIO_POLL_MAX_EVENTS 256
```
relevant only for epoll and kqueue - maximum number of events per review
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_EPOLL`
```c
#define FIO_POLL_ENGINE_EPOLL
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_KQUEUE`
```c
#define FIO_POLL_ENGINE_KQUEUE
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_POLL`
```c
#define FIO_POLL_ENGINE_POLL
```
_Symbol type:_ `macro`
#### `FIO_POLL_ENGINE_STR`
```c
#define FIO_POLL_ENGINE_STR "epoll"
```
_Symbol type:_ `macro`
#### `FIO_POLL_VALIDATE`
```c
#define FIO_POLL_VALIDATE(settings_dest) \
if (!(settings_dest).on_data) \
(settings_dest).on_data = fio___poll_ev_mock; \
if (!(settings_dest).on_ready) \
(settings_dest).on_ready = fio___poll_ev_mock; \
if (!(settings_dest).on_close) \
(settings_dest).on_close = fio___poll_ev_mock;
```
_Symbol type:_ `macro`
### Types
#### `fio_poll_s`
```c
struct fio_poll_s
```
the `fio_poll_s` type should be considered opaque.
_Symbol type:_ `type`
#### `fio_poll_settings_s`
```c
typedef struct {
/** callback for when data is available in the incoming buffer. */
void (*on_data)(void *udata);
/** callback for when the outgoing buffer allows a call to `write`. */
void (*on_ready)(void *udata);
/** callback for closed connections and / or connections with errors. */
void (*on_close)(void *udata);
} fio_poll_settings_s
```
_Symbol type:_ `type`
### Functions
#### `fio_poll_init`
```c
inline void fio_poll_init(fio_poll_s *p, fio_poll_settings_s)
```
Initializes the polling object, allocating its resources.
_Symbol type:_ `function`
#### `fio_poll_init`
```c
#define fio_poll_init(p, ...) \
fio_poll_init((p), (fio_poll_settings_s){__VA_ARGS__})
```
Initializes the polling object, allocating its resources.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_poll_destroy`
```c
inline void fio_poll_destroy(fio_poll_s *p)
```
Destroys the polling object, freeing its resources.
_Symbol type:_ `function`
#### `fio_poll_engine`
```c
inline const char *fio_poll_engine(void)
```
returns the system call used for polling as a constant string.
_Symbol type:_ `function`
#### `fio_poll_monitor`
```c
int fio_poll_monitor(fio_poll_s *p, fio_socket_i fd, void *udata, unsigned short flags)
```
Adds a file descriptor to be monitored, adds events to be monitored or
updates the monitored file's `udata`.
Possible flags are: `POLLIN` and `POLLOUT`. Other flags may be set but might
be ignored.
Monitoring mode is always one-shot. If an event if fired, it is removed from
the monitoring state.
Returns -1 on error.
_Symbol type:_ `function`
#### `fio_poll_review`
```c
int fio_poll_review(fio_poll_s *p, size_t timeout)
```
Reviews if any of the monitored file descriptors has any events.
`timeout` is in milliseconds.
Returns the number of events called.
Polling is thread safe, but has different effects on different threads.
Adding a new file descriptor from one thread while polling in a different
thread will not poll that IO until `fio_poll_review` is called again.
_Symbol type:_ `function`
#### `fio_poll_forget`
```c
int fio_poll_forget(fio_poll_s *p, fio_socket_i fd)
```
Stops monitoring the specified file descriptor (if monitoring).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/102 queue.h`
28 public symbols.
### Macros
#### `FIO_QUEUE_TASKS_PER_ALLOC`
```c
#define FIO_QUEUE_TASKS_PER_ALLOC 338
```
_Symbol type:_ `macro`
#### `FIO_QUEUE_STATIC_INIT`
```c
#define FIO_QUEUE_STATIC_INIT(queue) \
{ \
.r = &(queue).mem, .w = &(queue).mem, \
.lock = (fio_thread_mutex_t)FIO_THREAD_MUTEX_INIT, \
.consumers = FIO_LIST_INIT((queue).consumers), \
}
```
May be used to initialize global, static memory, queues.
_Symbol type:_ `macro`
#### `FIO_TIMER_QUEUE_INIT`
```c
#define FIO_TIMER_QUEUE_INIT \
{ .lock = ((fio_thread_mutex_t)FIO_THREAD_MUTEX_INIT) }
```
_Symbol type:_ `macro`
### Types
#### `fio_queue_task_s`
```c
typedef struct {
/** The function to call */
void (*fn)(void *, void *);
/** User opaque data */
void *udata1;
/** User opaque data */
void *udata2;
} fio_queue_task_s
```
Task information
_Symbol type:_ `type`
#### `fio_queue_s`
```c
typedef struct {
/** task read pointer. */
fio___task_ring_s *r;
/** task write pointer. */
fio___task_ring_s *w;
/** the number of tasks waiting to be performed. */
uint32_t count;
/** global queue lock. */
FIO___LOCK_TYPE lock;
/** linked lists of consumer threads. */
FIO_LIST_NODE consumers;
/** main ring buffer associated with the queue. */
fio___task_ring_s mem;
} fio_queue_s
```
The queue object - should be considered opaque (or, at least, read only).
_Symbol type:_ `type`
#### `fio_timer_queue_s`
```c
typedef struct {
fio___timer_event_s *next;
FIO___LOCK_TYPE lock;
} fio_timer_queue_s
```
_Symbol type:_ `type`
#### `fio_timer_schedule_args_s`
```c
typedef struct {
/** The timer function. If it returns a non-zero value, the timer stops. */
int (*fn)(void *, void *);
/** Opaque user data. */
void *udata1;
/** Opaque user data. */
void *udata2;
/** Called when the timer is done (finished). */
void (*on_finish)(void *, void *);
/** Timer interval, in milliseconds. */
uint32_t every;
/** The number of times the timer should be performed. -1 == infinity. */
int32_t repetitions;
/** Millisecond at which to start. If missing, filled automatically. */
int64_t start_at;
} fio_timer_schedule_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_queue_init`
```c
inline void fio_queue_init(fio_queue_s *q)
```
Initializes a fio_queue_s object.
_Symbol type:_ `function`
#### `fio_queue_destroy`
```c
void fio_queue_destroy(fio_queue_s *q)
```
Destroys a queue and re-initializes it, after freeing any used resources.
_Symbol type:_ `function`
#### `fio_queue_new`
```c
fio_queue_s *fio_queue_new(void)
```
Creates a new queue object (allocated on the heap).
_Symbol type:_ `function`
#### `fio_queue_free`
```c
void fio_queue_free(fio_queue_s *q)
```
Frees a queue object after calling fio_queue_destroy.
_Symbol type:_ `function`
#### `fio_queue_push`
```c
int fio_queue_push(fio_queue_s *q, fio_queue_task_s task)
```
Pushes a task to the queue. Returns -1 on error.
_Symbol type:_ `function`
#### `fio_queue_push`
```c
#define fio_queue_push(q, ...) \
fio_queue_push((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the queue, offering named arguments for the task.
Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_queue_push_urgent`
```c
int fio_queue_push_urgent(fio_queue_s *q, fio_queue_task_s task)
```
Pushes a task to the head of the queue. Returns -1 on error (no memory).
_Symbol type:_ `function`
#### `fio_queue_push_urgent`
```c
#define fio_queue_push_urgent(q, ...) \
fio_queue_push_urgent((q), (fio_queue_task_s){__VA_ARGS__})
```
Pushes a task to the queue, offering named arguments for the task.
Returns -1 on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_queue_pop`
```c
fio_queue_task_s fio_queue_pop(fio_queue_s *q)
```
Pops a task from the queue (FIFO). Returns a NULL task on error.
_Symbol type:_ `function`
#### `fio_queue_perform`
```c
int fio_queue_perform(fio_queue_s *q)
```
Performs a task from the queue. Returns -1 on error (queue empty).
_Symbol type:_ `function`
#### `fio_queue_perform_all`
```c
void fio_queue_perform_all(fio_queue_s *q)
```
Performs all tasks in the queue.
_Symbol type:_ `function`
#### `fio_queue_count`
```c
inline uint32_t fio_queue_count(fio_queue_s *q)
```
returns the number of tasks in the queue.
_Symbol type:_ `function`
#### `fio_queue_workers_add`
```c
int fio_queue_workers_add(fio_queue_s *q, size_t count)
```
Adds worker / consumer threads to perform the jobs in the queue.
_Symbol type:_ `function`
#### `fio_queue_workers_stop`
```c
size_t fio_queue_workers_stop(fio_queue_s *q)
```
Signals all worker threads to stop performing tasks and terminate.
Returns the number of worker groups signaled. Does NOT wait and does NOT
promise that pending tasks are performed.
_Symbol type:_ `function`
#### `fio_queue_workers_join`
```c
void fio_queue_workers_join(fio_queue_s *q)
```
Signals all worker threads to stop, waiting for them to complete.
If the queue had workers, pending tasks are performed before returning.
_Symbol type:_ `function`
#### `fio_queue_workers_wake`
```c
void fio_queue_workers_wake(fio_queue_s *q)
```
Signals all worker threads to go back to work (new tasks added).
_Symbol type:_ `function`
#### `fio_timer_schedule`
```c
void fio_timer_schedule(fio_timer_queue_s *timer_queue, fio_timer_schedule_args_s args)
```
Adds a time-bound event to the timer queue.
_Symbol type:_ `function`
#### `fio_timer_schedule`
```c
#define fio_timer_schedule(timer_queue, ...) \
fio_timer_schedule((timer_queue), (fio_timer_schedule_args_s){__VA_ARGS__})
```
A MACRO allowing named arguments to be used. See fio_timer_schedule_args_s.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_timer_push2queue`
```c
size_t fio_timer_push2queue(fio_queue_s *queue, fio_timer_queue_s *timer_queue, int64_t now_in_milliseconds)
```
Pushes due events from the timer queue to an event queue.
_Symbol type:_ `function`
#### `fio_timer_next_at`
```c
inline int64_t fio_timer_next_at(fio_timer_queue_s *timer_queue)
```
_Symbol type:_ `function`
#### `fio_timer_destroy`
```c
void fio_timer_destroy(fio_timer_queue_s *timer_queue)
```
Clears any waiting timer bound tasks.
NOTE:
The timer queue must NEVER be freed when there's a chance that timer tasks
are waiting to be performed in a `fio_queue_s`.
This is due to the fact that the tasks may try to reschedule themselves (if
they repeat).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/102 stream.h`
16 public symbols.
### Macros
#### `FIO_STREAM_COPY_PER_PACKET`
```c
#define FIO_STREAM_COPY_PER_PACKET 98304
```
Break apart large memory blocks into smaller pieces. by default 96Kb
_Symbol type:_ `macro`
#### `FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN`
```c
#define FIO_STREAM_ALWAYS_COPY_IF_LESS_THAN 116
```
If the data added is less than said bytes, copy is preferred (locality).
_Symbol type:_ `macro`
#### `FIO_STREAM_INIT`
```c
#define FIO_STREAM_INIT(s) \
{ .next = NULL, .pos = &(s).next }
```
_Symbol type:_ `macro`
### Types
#### `fio_stream_packet_s`
```c
struct fio_stream_packet_s
```
_Symbol type:_ `type`
#### `fio_stream_s`
```c
typedef struct {
/* do not directly access! */
fio_stream_packet_s *next;
fio_stream_packet_s **pos;
size_t consumed;
size_t length;
} fio_stream_s
```
_Symbol type:_ `type`
### Functions
#### `fio_stream_new`
```c
inline fio_stream_s *fio_stream_new(void)
```
_Symbol type:_ `function`
#### `fio_stream_free`
```c
inline int fio_stream_free(fio_stream_s *stream)
```
_Symbol type:_ `function`
#### `fio_stream_destroy`
```c
void fio_stream_destroy(fio_stream_s *stream)
```
Destroys the object, re-initializing its container.
_Symbol type:_ `function`
#### `fio_stream_pack_data`
```c
fio_stream_packet_s *fio_stream_pack_data(void *buf, size_t len, size_t offset, uint8_t copy_buffer, void (*dealloc_func)(void *))
```
Packs data into a fio_stream_packet_s container.
_Symbol type:_ `function`
#### `fio_stream_pack_fd`
```c
fio_stream_packet_s *fio_stream_pack_fd(int fd, size_t len, size_t offset, uint8_t keep_open)
```
Packs a file descriptor into a fio_stream_packet_s container.
_Symbol type:_ `function`
#### `fio_stream_add`
```c
void fio_stream_add(fio_stream_s *stream, fio_stream_packet_s *packet)
```
Adds a packet to the stream. This isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_pack_free`
```c
void fio_stream_pack_free(fio_stream_packet_s *packet)
```
Destroys the fio_stream_packet_s - call this ONLY if unused.
_Symbol type:_ `function`
#### `fio_stream_read`
```c
void fio_stream_read(fio_stream_s *stream, char **buf, size_t *len)
```
Reads data from the stream (if any), leaving it in the stream.
`buf` MUST point to a buffer with - at least - `len` bytes. This is required
in case the packed data is fragmented or references a file and needs to be
copied to an available buffer.
On error, or if the stream is empty, `buf` will be set to NULL and `len` will
be set to zero.
Otherwise, `buf` may retain the same value or it may point directly to a
memory address within the stream's buffer (the original value may be lost)
and `len` will be updated to the largest possible value for valid data that
can be read from `buf`.
Note: this isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_advance`
```c
void fio_stream_advance(fio_stream_s *stream, size_t len)
```
Advances the Stream, so the first `len` bytes are marked as consumed.
Note: this isn't thread safe.
_Symbol type:_ `function`
#### `fio_stream_any`
```c
inline uint8_t fio_stream_any(fio_stream_s *stream)
```
Returns true if there's any data in the stream.
Note: this isn't truly thread safe.
_Symbol type:_ `function`
#### `fio_stream_length`
```c
inline size_t fio_stream_length(fio_stream_s *stream)
```
Returns the number of bytes waiting in the stream.
Note: this isn't truly thread safe.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/103 md2html.h`
2 public symbols.
### Macros
#### `FIO_MD2HTML`
```c
#define FIO_MD2HTML /* Development inclusion - ignore line */
```
_Symbol type:_ `macro`
#### `FIO_MD2HTML_ERR_ALLOC`
```c
#define FIO_MD2HTML_ERR_ALLOC 1
```
Markdown-to-HTML renderer callback error: output allocation failed.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/104 mustache.h`
14 public symbols.
### Macros
#### `FIO_MUSTACHE_MAX_DEPTH`
```c
#define FIO_MUSTACHE_MAX_DEPTH 128
```
The maximum depth of a template's context
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_PRESERVE_PADDING`
```c
#define FIO_MUSTACHE_PRESERVE_PADDING 0
```
Preserves padding for stand-alone variables and partial templates
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_LAMBDA_SUPPORT`
```c
#define FIO_MUSTACHE_LAMBDA_SUPPORT 0
```
Supports raw text for lambda style languages.
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_ISOLATE_PARTIALS`
```c
#define FIO_MUSTACHE_ISOLATE_PARTIALS 1
```
Limits the scope of partial templates to the context of their section.
_Symbol type:_ `macro`
#### `FIO_MUSTACHE_SECURE_PATH`
```c
#define FIO_MUSTACHE_SECURE_PATH 1
```
_Symbol type:_ `macro`
### Types
#### `fio_mustache_s`
```c
struct fio_mustache_s
```
_Symbol type:_ `type`
#### `fio_mustache_bargs_s`
```c
struct fio_mustache_bargs_s
```
_Symbol type:_ `type`
#### `fio_mustache_load_args_s`
```c
typedef struct {
/** The file's content (if pre-loaded) */
fio_buf_info_s data;
/** The file's name (even if preloaded, used for partials load paths) */
fio_buf_info_s filename;
/** Loads the file's content, returning a `fio_buf_info_s` structure. */
fio_buf_info_s (*load_file_data)(fio_buf_info_s filename, void *udata);
/** Frees the file's content from its `fio_buf_info_s` structure. */
void (*free_file_data)(fio_buf_info_s file_data, void *udata);
/** Called when YAML front matter data was found. */
void (*on_yaml_front_matter)(fio_buf_info_s yaml_front_matter, void *udata);
/** Opaque user data. */
void *udata;
} fio_mustache_load_args_s
```
_Symbol type:_ `type`
### Functions
#### `fio_mustache_load`
```c
fio_mustache_s *fio_mustache_load(fio_mustache_load_args_s settings)
```
_Symbol type:_ `function`
#### `fio_mustache_load`
```c
#define fio_mustache_load(...) \
fio_mustache_load((fio_mustache_load_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_mustache_free`
```c
void fio_mustache_free(fio_mustache_s *m)
```
_Symbol type:_ `function`
#### `fio_mustache_dup`
```c
fio_mustache_s *fio_mustache_dup(fio_mustache_s *m)
```
Increases the mustache template's reference count.
_Symbol type:_ `function`
#### `fio_mustache_build`
```c
void *fio_mustache_build(fio_mustache_s *m, fio_mustache_bargs_s)
```
Builds the template, returning the final value of `udata` (or NULL).
_Symbol type:_ `function`
#### `fio_mustache_build`
```c
#define fio_mustache_build(m, ...) \
fio_mustache_build((m), ((fio_mustache_bargs_s){__VA_ARGS__}))
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/150 crypto core.h`
2 public symbols.
### Types
#### `fio_crypto_enc_fn`
```c
typedef void(fio_crypto_enc_fn)(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
_Symbol type:_ `type`
#### `fio_crypto_dec_fn`
```c
typedef int(fio_crypto_dec_fn)(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
_Symbol type:_ `type`
-----------------------------------------------------
# `./fio-stl/152 blake2.h`
14 public symbols.
### Types
#### `fio_blake2b_s`
```c
typedef struct {
uint64_t h[8]; /* state */
uint64_t t[2]; /* total bytes processed (128-bit counter) */
uint64_t f[2]; /* finalization flags */
uint8_t buf[128]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2b_s
```
Streaming BLAKE2b type (64-bit, up to 64-byte digest).
_Symbol type:_ `type`
#### `fio_blake2s_s`
```c
typedef struct {
uint32_t h[8]; /* state */
uint32_t t[2]; /* total bytes processed (64-bit counter) */
uint32_t f[2]; /* finalization flags */
uint8_t buf[64]; /* input buffer */
size_t buflen; /* bytes in buffer */
size_t outlen; /* digest length */
} fio_blake2s_s
```
Streaming BLAKE2s type (32-bit, up to 32-byte digest).
_Symbol type:_ `type`
### Functions
#### `fio_blake2b`
```c
inline fio_u512 fio_blake2b(const void *data, uint64_t len)
```
One-shot BLAKE2b hash with max-length (64 byte) digest.
_Symbol type:_ `function`
#### `fio_blake2b_hash`
```c
void fio_blake2b_hash(void *restrict out, size_t outlen, const void *restrict data, size_t len, const void *restrict key, size_t keylen)
```
Flexible-output BLAKE2b hash.
`out` must point to a buffer of at least `outlen` bytes.
`outlen` must be between 1 and 64 (default 64 if 0).
`key` and `keylen` are optional (set to NULL/0 for unkeyed hashing).
_Symbol type:_ `function`
#### `fio_blake2b_hmac`
```c
fio_u512 fio_blake2b_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
HMAC-BLAKE2b (64 byte key, 64 byte digest), compatible with SHA HMAC.
_Symbol type:_ `function`
#### `fio_blake2b_init`
```c
fio_blake2b_s fio_blake2b_init(size_t outlen, const void *key, size_t keylen)
```
Initialize a BLAKE2b streaming context. outlen: 1-64 (default 64).
_Symbol type:_ `function`
#### `fio_blake2b_consume`
```c
void fio_blake2b_consume(fio_blake2b_s *restrict h, const void *restrict data, size_t len)
```
Feed data into BLAKE2b hash.
_Symbol type:_ `function`
#### `fio_blake2b_finalize`
```c
fio_u512 fio_blake2b_finalize(fio_blake2b_s *h)
```
Finalize BLAKE2b hash. Returns result in fio_u512 (valid bytes = outlen).
_Symbol type:_ `function`
#### `fio_blake2s`
```c
inline fio_u256 fio_blake2s(const void *data, uint64_t len)
```
One-shot BLAKE2s hash with max-length (32 byte) digest.
_Symbol type:_ `function`
#### `fio_blake2s_hash`
```c
void fio_blake2s_hash(void *restrict out, size_t outlen, const void *restrict data, size_t len, const void *restrict key, size_t keylen)
```
Flexible-output BLAKE2s hash.
`out` must point to a buffer of at least `outlen` bytes.
`outlen` must be between 1 and 32 (default 32 if 0).
`key` and `keylen` are optional (set to NULL/0 for unkeyed hashing).
_Symbol type:_ `function`
#### `fio_blake2s_hmac`
```c
fio_u256 fio_blake2s_hmac(const void *key, uint64_t key_len, const void *msg, uint64_t msg_len)
```
HMAC-BLAKE2s (32 byte key, 32 byte digest), compatible with SHA HMAC.
_Symbol type:_ `function`
#### `fio_blake2s_init`
```c
fio_blake2s_s fio_blake2s_init(size_t outlen, const void *key, size_t keylen)
```
Initialize a BLAKE2s streaming context. outlen: 1-32 (default 32).
_Symbol type:_ `function`
#### `fio_blake2s_consume`
```c
void fio_blake2s_consume(fio_blake2s_s *restrict h, const void *restrict data, size_t len)
```
Feed data into BLAKE2s hash.
_Symbol type:_ `function`
#### `fio_blake2s_finalize`
```c
fio_u256 fio_blake2s_finalize(fio_blake2s_s *h)
```
Finalize BLAKE2s hash. Returns result in fio_u256 (valid bytes = outlen).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 chacha20poly1305.h`
7 public symbols.
### Functions
#### `fio_chacha20_poly1305_enc`
```c
void fio_chacha20_poly1305_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place encryption of `data` using ChaCha20 with additional
data, producing a 16 byte message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
_Symbol type:_ `function`
#### `fio_chacha20_poly1305_dec`
```c
int fio_chacha20_poly1305_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place decryption of `data` using ChaCha20 after authenticating
the message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
* `mac` MUST point to a buffer where the 16 byte MAC is placed.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_xchacha20_poly1305_enc`
```c
void fio_xchacha20_poly1305_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place encryption of `data` using XChaCha20 with additional
data, producing a 16 byte message authentication code (MAC) using Poly1305.
XChaCha20 uses a 192-bit (24 byte) nonce, making it safe to use with
randomly-generated nonces without risk of nonce collision.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
_Symbol type:_ `function`
#### `fio_xchacha20_poly1305_dec`
```c
int fio_xchacha20_poly1305_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, /* additional data */ size_t adlen, const void *key, const void *nonce)
```
Performs an in-place decryption of `data` using XChaCha20 after
authenticating the message authentication code (MAC) using Poly1305.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
* `mac` MUST point to a buffer where the 16 byte MAC is placed.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_xchacha20`
```c
void fio_xchacha20(void *restrict data, size_t len, const void *key, const void *nonce, uint32_t counter)
```
Performs an in-place encryption/decryption of `data` using XChaCha20.
XChaCha20 uses a 192-bit (24 byte) nonce, making it safe to use with
randomly-generated nonces without risk of nonce collision.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 192 bit long memory address (24 Bytes).
* `counter` is the block counter, usually 0 unless `data` is mid-cyphertext.
_Symbol type:_ `function`
#### `fio_chacha20`
```c
void fio_chacha20(void *restrict data, size_t len, const void *key, const void *nonce, uint32_t counter)
```
Performs an in-place encryption/decryption of `data` using ChaCha20.
* `key` MUST point to a 256 bit long memory address (32 Bytes).
* `nonce` MUST point to a 96 bit long memory address (12 Bytes).
* `counter` is the block counter, usually 1 unless `data` is mid-cyphertext.
_Symbol type:_ `function`
#### `fio_poly1305_auth`
```c
void fio_poly1305_auth(void *restrict mac_dest, void *restrict message, size_t len, const void *ad, size_t ad_len, const void *key256bits)
```
Given a Poly1305 256bit (32 byte) key, writes the authentication code for the
poly message and additional data into `mac_dest`.
* `mac_dest` MUST point to a buffer with (at least) 16 available bytes.
* `message` MAY be omitted.
* `ad` MAY be omitted (additional data).
* `key` MUST point to a 256 bit long memory address (32 Bytes).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha1.h`
4 public symbols.
### Types
#### `fio_sha1_s`
```c
typedef union {
#ifdef __SIZEOF_INT128__
__uint128_t align__;
#else
uint64_t align__;
#endif
uint32_t v[5];
uint8_t digest[20];
} fio_sha1_s
```
The data type containing the SHA1 digest (result).
_Symbol type:_ `type`
### Functions
#### `fio_sha1`
```c
fio_sha1_s fio_sha1(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA1 hashing algorithm.
Do NOT use - SHA1 is broken... but for some reason some protocols still
require it's use (i.e., WebSockets), so it's here for your convenience.
_Symbol type:_ `function`
#### `fio_sha1_len`
```c
inline size_t fio_sha1_len(void)
```
Returns the digest length of SHA1 in bytes (20 bytes)
_Symbol type:_ `function`
#### `fio_sha1_digest`
```c
inline uint8_t *fio_sha1_digest(fio_sha1_s *s)
```
Returns the 20 Byte long digest of a SHA1 object.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha2.h`
14 public symbols.
### Types
#### `fio_sha256_s`
```c
typedef struct {
fio_u256 hash;
fio_u512 cache;
uint64_t total_len;
} fio_sha256_s
```
Streaming SHA-256 type.
_Symbol type:_ `type`
#### `fio_sha512_s`
```c
typedef struct {
fio_u512 hash;
fio_u1024 cache;
uint64_t total_len;
} fio_sha512_s
```
Streaming SHA-512 type.
_Symbol type:_ `type`
### Functions
#### `fio_sha256`
```c
inline fio_u256 fio_sha256(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-256 hashing algorithm.
_Symbol type:_ `function`
#### `fio_sha256_init`
```c
inline fio_sha256_s fio_sha256_init(void)
```
initializes a fio_u256 so the hash can consume streaming data.
_Symbol type:_ `function`
#### `fio_sha256_consume`
```c
void fio_sha256_consume(fio_sha256_s *h, const void *data, uint64_t len)
```
Feed data into the hash
_Symbol type:_ `function`
#### `fio_sha256_finalize`
```c
fio_u256 fio_sha256_finalize(fio_sha256_s *h)
```
finalizes a fio_u256 with the SHA 256 hash.
_Symbol type:_ `function`
#### `fio_sha512`
```c
inline fio_u512 fio_sha512(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-512 hashing algorithm.
_Symbol type:_ `function`
#### `fio_sha512_init`
```c
inline fio_sha512_s fio_sha512_init(void)
```
initializes a fio_u512 so the hash can consume streaming data.
_Symbol type:_ `function`
#### `fio_sha512_consume`
```c
void fio_sha512_consume(fio_sha512_s *h, const void *data, uint64_t len)
```
Feed data into the hash
_Symbol type:_ `function`
#### `fio_sha512_finalize`
```c
fio_u512 fio_sha512_finalize(fio_sha512_s *h)
```
finalizes a fio_u512 with the SHA 512 hash.
_Symbol type:_ `function`
#### `fio_sha384_init`
```c
inline fio_sha512_s fio_sha384_init(void)
```
initializes a fio_u512 so the hash can consume streaming data (SHA-384).
SHA-384 is SHA-512 with different initial values, truncated to 48 bytes.
NOTE: truncating a SHA-512 result is NOT the same as SHA-384.
_Symbol type:_ `function`
#### `fio_sha384_consume`
```c
#define fio_sha384_consume fio_sha512_consume
```
Feed data into the hash
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sha384_finalize`
```c
#define fio_sha384_finalize fio_sha512_finalize
```
finalizes a fio_u512 with the SHA 384 hash (first 48 bytes are the hash).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_sha384`
```c
inline fio_u512 fio_sha384(const void *data, uint64_t len)
```
A simple, non streaming, implementation of the SHA-384 hashing algorithm.
Returns a fio_u512 of which the first 48 bytes hold the SHA-384 hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha2z hkdf.h`
5 public symbols.
### Macros
#### `FIO_HKDF_SHA256_HASH_LEN`
```c
#define FIO_HKDF_SHA256_HASH_LEN 32
```
SHA-256 hash length (32 bytes).
_Symbol type:_ `macro`
#### `FIO_HKDF_SHA384_HASH_LEN`
```c
#define FIO_HKDF_SHA384_HASH_LEN 48
```
SHA-384 hash length (48 bytes).
_Symbol type:_ `macro`
### Functions
#### `fio_hkdf_extract`
```c
void fio_hkdf_extract(void *restrict prk, const void *restrict salt, size_t salt_len, const void *restrict ikm, size_t ikm_len, int use_sha384)
```
HKDF-Extract: PRK = HMAC-Hash(salt, IKM)
Extracts a pseudorandom key (PRK) from input keying material (IKM).
**Parameters:**
- `prk` - Output buffer (32 bytes for SHA-256, 48 for SHA-384)
- `salt` - Optional salt (if NULL, uses zeros of hash length)
- `salt_len` - Salt length in bytes
- `ikm` - Input keying material
- `ikm_len` - IKM length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_hkdf_expand`
```c
void fio_hkdf_expand(void *restrict okm, size_t okm_len, const void *restrict prk, size_t prk_len, const void *restrict info, size_t info_len, int use_sha384)
```
HKDF-Expand: OKM = HKDF-Expand(PRK, info, L)
Expands a pseudorandom key (PRK) into output keying material (OKM).
**Parameters:**
- `okm` - Output keying material buffer
- `okm_len` - Desired output length (max 255 * hash_len)
- `prk` - Pseudorandom key from Extract (32 or 48 bytes)
- `prk_len` - PRK length (32 for SHA-256, 48 for SHA-384)
- `info` - Optional context/application info
- `info_len` - Info length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_hkdf`
```c
void fio_hkdf(void *restrict okm, size_t okm_len, const void *restrict salt, size_t salt_len, const void *restrict ikm, size_t ikm_len, const void *restrict info, size_t info_len, int use_sha384)
```
Combined HKDF (Extract + Expand) - RFC 5869 Section 2.
Derives keying material from input keying material using HKDF.
**Parameters:**
- `okm` - Output keying material buffer
- `okm_len` - Desired output length (max 255 * hash_len)
- `salt` - Optional salt (if NULL, uses zeros of hash length)
- `salt_len` - Salt length in bytes
- `ikm` - Input keying material
- `ikm_len` - IKM length in bytes
- `info` - Optional context/application info
- `info_len` - Info length in bytes
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/152 sha3.h`
17 public symbols.
### Types
#### `fio_sha3_s`
```c
typedef struct {
uint64_t state[25];
uint8_t buf[200]; /* rate buffer (max rate = 1600 - 2*capacity) */
size_t buflen; /* bytes in buffer */
size_t rate; /* rate in bytes */
size_t outlen; /* output length in bytes (0 for SHAKE) */
uint8_t delim; /* domain separation byte */
} fio_sha3_s
```
Keccak state (1600 bits = 200 bytes = 25 x 64-bit words)
_Symbol type:_ `type`
### Functions
#### `fio_sha3_224_init`
```c
inline fio_sha3_s fio_sha3_224_init(void)
```
Initialize SHA3-224 (28-byte output).
_Symbol type:_ `function`
#### `fio_sha3_256_init`
```c
inline fio_sha3_s fio_sha3_256_init(void)
```
Initialize SHA3-256 (32-byte output).
_Symbol type:_ `function`
#### `fio_sha3_384_init`
```c
inline fio_sha3_s fio_sha3_384_init(void)
```
Initialize SHA3-384 (48-byte output).
_Symbol type:_ `function`
#### `fio_sha3_512_init`
```c
inline fio_sha3_s fio_sha3_512_init(void)
```
Initialize SHA3-512 (64-byte output).
_Symbol type:_ `function`
#### `fio_sha3_consume`
```c
void fio_sha3_consume(fio_sha3_s *restrict h, const void *restrict data, size_t len)
```
Feed data into SHA3 hash.
_Symbol type:_ `function`
#### `fio_sha3_finalize`
```c
void fio_sha3_finalize(fio_sha3_s *restrict h, void *restrict out)
```
Finalize SHA3 hash. Writes h->outlen bytes to out.
_Symbol type:_ `function`
#### `fio_sha3_224`
```c
void fio_sha3_224(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-224 (28-byte output).
_Symbol type:_ `function`
#### `fio_sha3_256`
```c
void fio_sha3_256(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-256 (32-byte output).
_Symbol type:_ `function`
#### `fio_sha3_384`
```c
void fio_sha3_384(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-384 (48-byte output).
_Symbol type:_ `function`
#### `fio_sha3_512`
```c
void fio_sha3_512(void *restrict out, const void *restrict data, size_t len)
```
Simple SHA3-512 (64-byte output).
_Symbol type:_ `function`
#### `fio_shake128_init`
```c
inline fio_sha3_s fio_shake128_init(void)
```
Initialize SHAKE128 (variable output length).
_Symbol type:_ `function`
#### `fio_shake256_init`
```c
inline fio_sha3_s fio_shake256_init(void)
```
Initialize SHAKE256 (variable output length).
_Symbol type:_ `function`
#### `fio_shake_consume`
```c
#define fio_shake_consume fio_sha3_consume
```
Feed data into SHAKE.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_shake_squeeze`
```c
void fio_shake_squeeze(fio_sha3_s *restrict h, void *restrict out, size_t outlen)
```
Squeeze output from SHAKE. Can be called multiple times.
_Symbol type:_ `function`
#### `fio_shake128`
```c
void fio_shake128(void *restrict out, size_t outlen, const void *restrict data, size_t len)
```
Simple SHAKE128 with specified output length.
_Symbol type:_ `function`
#### `fio_shake256`
```c
void fio_shake256(void *restrict out, size_t outlen, const void *restrict data, size_t len)
```
Simple SHAKE256 with specified output length.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/153 aes.h`
4 public symbols.
### Functions
#### `fio_aes128_gcm_enc`
```c
void fio_aes128_gcm_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-128-GCM encryption with authentication.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
* `key` MUST point to a 128 bit key (16 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
_Symbol type:_ `function`
#### `fio_aes128_gcm_dec`
```c
int fio_aes128_gcm_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-128-GCM decryption with authentication verification.
* `mac` MUST point to a buffer with the 16 byte MAC to verify.
* `key` MUST point to a 128 bit key (16 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
#### `fio_aes256_gcm_enc`
```c
void fio_aes256_gcm_enc(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-256-GCM encryption with authentication.
* `mac` MUST point to a buffer with (at least) 16 available bytes.
* `key` MUST point to a 256 bit key (32 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted, will NOT be encrypted.
* `data` MAY be omitted, WILL be encrypted.
_Symbol type:_ `function`
#### `fio_aes256_gcm_dec`
```c
int fio_aes256_gcm_dec(void *restrict mac, void *restrict data, size_t len, const void *ad, size_t adlen, const void *key, const void *nonce)
```
Performs in-place AES-256-GCM decryption with authentication verification.
* `mac` MUST point to a buffer with the 16 byte MAC to verify.
* `key` MUST point to a 256 bit key (32 Bytes).
* `nonce` MUST point to a 96 bit nonce (12 Bytes).
* `ad` MAY be omitted ONLY IF originally omitted.
* `data` MAY be omitted, WILL be decrypted.
Returns `-1` on error (authentication failed).
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 ed25519.h`
13 public symbols.
### Macros
#### `FIO_X25519_CIPHERTEXT_LEN`
```c
#define FIO_X25519_CIPHERTEXT_LEN(message_len) ((message_len) + 48)
```
Returns the ciphertext length for a given plaintext length.
Ciphertext = ephemeral_pk (32) + mac (16) + encrypted_message (message_len)
_Symbol type:_ `macro`
#### `FIO_X25519_PLAINTEXT_LEN`
```c
#define FIO_X25519_PLAINTEXT_LEN(ciphertext_len) \
((ciphertext_len) > 48 ? ((ciphertext_len)-48) : 0)
```
Returns the plaintext length for a given ciphertext length.
Returns 0 if ciphertext_len < 48 (invalid ciphertext).
_Symbol type:_ `macro`
### Functions
#### `fio_ed25519_keypair`
```c
void fio_ed25519_keypair(uint8_t secret_key[32], uint8_t public_key[32])
```
Generates a new random Ed25519 key pair.
The secret key must be kept secret and securely erased when no longer
needed. The public key can be freely shared.
_Symbol type:_ `function`
#### `fio_ed25519_public_key`
```c
void fio_ed25519_public_key(uint8_t public_key[32], const uint8_t secret_key[32])
```
Derives the public key from an Ed25519 secret key.
Useful when the secret key is loaded from storage and the public key
needs to be recomputed.
_Symbol type:_ `function`
#### `fio_ed25519_sign`
```c
int fio_ed25519_sign(uint8_t signature[64], const void *message, size_t len, const uint8_t secret_key[32], const uint8_t public_key[32])
```
Signs a message using Ed25519.
The signature is 64 bytes and is deterministic (same message + key = same
signature).
_Symbol type:_ `function`
#### `fio_ed25519_verify`
```c
int fio_ed25519_verify(const uint8_t signature[64], const void *message, size_t len, const uint8_t public_key[32])
```
Verifies an Ed25519 signature.
Returns 0 on success (valid signature), -1 on failure (invalid signature).
_Symbol type:_ `function`
#### `fio_x25519_keypair`
```c
void fio_x25519_keypair(uint8_t secret_key[32], uint8_t public_key[32])
```
Generates a new random X25519 key pair.
The secret key must be kept secret. The public key can be shared with
the other party for key exchange.
_Symbol type:_ `function`
#### `fio_x25519_public_key`
```c
void fio_x25519_public_key(uint8_t public_key[32], const uint8_t secret_key[32])
```
Derives the public key from an X25519 secret key.
This performs scalar multiplication of the secret key with the base point.
_Symbol type:_ `function`
#### `fio_x25519_shared_secret`
```c
int fio_x25519_shared_secret(uint8_t shared_secret[32], const uint8_t secret_key[32], const uint8_t their_public_key[32])
```
Computes a shared secret using X25519 (ECDH).
Both parties compute the same shared secret:
shared = X25519(my_secret, their_public)
The shared secret should be passed through a KDF (e.g., HKDF with SHA-256)
before being used as an encryption key.
Returns 0 on success, -1 on failure (e.g., if their_public is a low-order
point, which would result in an all-zero shared secret).
_Symbol type:_ `function`
#### `fio_ed25519_sk_to_x25519`
```c
void fio_ed25519_sk_to_x25519(uint8_t x_secret_key[32], const uint8_t ed_secret_key[32])
```
Converts an Ed25519 secret key to an X25519 secret key.
This allows using an Ed25519 signing key for X25519 key exchange.
_Symbol type:_ `function`
#### `fio_ed25519_pk_to_x25519`
```c
void fio_ed25519_pk_to_x25519(uint8_t x_public_key[32], const uint8_t ed_public_key[32])
```
Converts an Ed25519 public key to an X25519 public key.
This allows encrypting to someone who has only shared their Ed25519
signing public key.
_Symbol type:_ `function`
#### `fio_x25519_encrypt`
```c
int fio_x25519_encrypt(uint8_t *ciphertext, const void *message, size_t message_len, fio_crypto_enc_fn encryption_function, const uint8_t recipient_pk[32])
```
Encrypts a message using the recipient's X25519 public key.
The ciphertext includes:
- 32 bytes: ephemeral public key (for key agreement)
- 16 bytes: authentication tag (MAC)
- N bytes: encrypted message
Total ciphertext size = message_len + 48 bytes
**Parameters:**
- `ciphertext` - Output buffer (must be at least message_len + 48 bytes)
- `message` - The plaintext message to encrypt
- `message_len` - Length of the message
- `encryption_function` - Encryption function (fio_chacha20_poly1305_enc)
- `recipient_pk` - The recipient's X25519 public key (32 bytes)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_x25519_decrypt`
```c
int fio_x25519_decrypt(uint8_t *plaintext, const uint8_t *ciphertext, size_t ciphertext_len, fio_crypto_dec_fn decryption_function, const uint8_t recipient_sk[32])
```
Decrypts a message using the recipient's X25519 secret key.
**Parameters:**
- `plaintext` - Output buffer (must be at least ciphertext_len - 48 bytes)
- `ciphertext` - The ciphertext (ephemeral_pk || mac || encrypted_data)
- `ciphertext_len` - Length of the ciphertext (must be >= 48)
- `decryption_function` - Decryption function (fio_chacha20_poly1305_dec)
- `recipient_sk` - The recipient's X25519 secret key (32 bytes)
**Returns:**
- 0 on success, -1 on failure (authentication failed or invalid input)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 p256.h`
5 public symbols.
### Functions
#### `fio_ecdsa_p256_verify`
```c
int fio_ecdsa_p256_verify(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, const uint8_t *pubkey, size_t pubkey_len)
```
Verifies an ECDSA P-256 signature.
**Parameters:**
- `sig` - DER-encoded signature (SEQUENCE { r INTEGER, s INTEGER })
- `sig_len` - Length of signature in bytes
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `pubkey` - Uncompressed public key (65 bytes: 0x04 || x || y)
- `pubkey_len` - Length of public key (must be 65)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_ecdsa_p256_sign`
```c
int fio_ecdsa_p256_sign(uint8_t *sig, size_t *sig_len, size_t sig_capacity, const uint8_t msg_hash[32], const uint8_t secret_key[32])
```
Signs a message hash using ECDSA P-256.
**Parameters:**
- `sig` - Output: DER-encoded signature (max 72 bytes)
- `sig_len` - Output: actual signature length
- `sig_capacity` - Capacity of signature buffer (should be >= 72)
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `secret_key` - 32-byte secret key (scalar, big-endian)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_ecdsa_p256_verify_raw`
```c
int fio_ecdsa_p256_verify_raw(const uint8_t r[32], const uint8_t s[32], const uint8_t msg_hash[32], const uint8_t pubkey_x[32], const uint8_t pubkey_y[32])
```
Verifies an ECDSA P-256 signature with raw r,s values.
**Parameters:**
- `r` - The r component of the signature (32 bytes, big-endian)
- `s` - The s component of the signature (32 bytes, big-endian)
- `msg_hash` - SHA-256 hash of the message (32 bytes)
- `pubkey_x` - X coordinate of public key (32 bytes, big-endian)
- `pubkey_y` - Y coordinate of public key (32 bytes, big-endian)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_p256_keypair`
```c
int fio_p256_keypair(uint8_t secret_key[32], uint8_t public_key[65])
```
Generates a P-256 keypair for ECDHE key exchange.
**Parameters:**
- `secret_key` - Output: 32-byte secret key (scalar, big-endian)
- `public_key` - Output: 65-byte uncompressed public key (0x04 || x || y)
**Returns:**
- 0 on success, -1 on failure
_Symbol type:_ `function`
#### `fio_p256_shared_secret`
```c
int fio_p256_shared_secret(uint8_t shared_secret[32], const uint8_t secret_key[32], const uint8_t *their_public_key, size_t their_public_key_len)
```
Computes P-256 ECDH shared secret.
**Parameters:**
- `shared_secret` - Output: 32-byte shared secret (x-coordinate of result)
- `secret_key` - 32-byte secret key (scalar, big-endian)
- `their_public_key` - Their public key (uncompressed 65 bytes, or compressed 33 bytes)
- `their_public_key_len` - Length of their public key (33 or 65)
**Returns:**
- 0 on success, -1 on failure (invalid key, point at infinity, etc.)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/154 p384.h`
2 public symbols.
### Functions
#### `fio_ecdsa_p384_verify`
```c
int fio_ecdsa_p384_verify(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, const uint8_t *pubkey, size_t pubkey_len)
```
Verifies an ECDSA P-384 signature.
**Parameters:**
- `sig` - DER-encoded signature (SEQUENCE { r INTEGER, s INTEGER })
- `sig_len` - Length of signature in bytes
- `msg_hash` - SHA-384 hash of the message (48 bytes)
- `pubkey` - Uncompressed public key (97 bytes: 0x04 || x || y)
- `pubkey_len` - Length of public key (must be 97)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
#### `fio_ecdsa_p384_verify_raw`
```c
int fio_ecdsa_p384_verify_raw(const uint8_t r[48], const uint8_t s[48], const uint8_t msg_hash[48], const uint8_t pubkey_x[48], const uint8_t pubkey_y[48])
```
Verifies an ECDSA P-384 signature with raw r,s values.
**Parameters:**
- `r` - The r component of the signature (48 bytes, big-endian)
- `s` - The s component of the signature (48 bytes, big-endian)
- `msg_hash` - SHA-384 hash of the message (48 bytes)
- `pubkey_x` - X coordinate of public key (48 bytes, big-endian)
- `pubkey_y` - Y coordinate of public key (48 bytes, big-endian)
**Returns:**
- 0 on success (valid signature), -1 on failure (invalid signature)
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/155 der.h`
18 public symbols.
### Types
#### `fio_der_tag_e`
```c
typedef enum {
FIO_DER_EOC = 0x00, /**< End-of-contents */
FIO_DER_BOOLEAN = 0x01, /**< Boolean */
FIO_DER_INTEGER = 0x02, /**< Integer */
FIO_DER_BIT_STRING = 0x03, /**< Bit String */
FIO_DER_OCTET_STRING = 0x04, /**< Octet String */
FIO_DER_NULL = 0x05, /**< Null */
FIO_DER_OID = 0x06, /**< Object Identifier */
FIO_DER_OBJECT_DESCRIPTOR = 0x07, /**< Object Descriptor */
FIO_DER_EXTERNAL = 0x08, /**< External */
FIO_DER_REAL = 0x09, /**< Real (float) */
FIO_DER_ENUMERATED = 0x0A, /**< Enumerated */
FIO_DER_EMBEDDED_PDV = 0x0B, /**< Embedded PDV */
FIO_DER_UTF8_STRING = 0x0C, /**< UTF-8 String */
FIO_DER_RELATIVE_OID = 0x0D, /**< Relative OID */
FIO_DER_SEQUENCE = 0x10, /**< Sequence (0x30 with constructed bit) */
FIO_DER_SET = 0x11, /**< Set (0x31 with constructed bit) */
FIO_DER_NUMERIC_STRING = 0x12, /**< Numeric String */
FIO_DER_PRINTABLE_STRING = 0x13, /**< Printable String */
FIO_DER_T61_STRING = 0x14, /**< T61 String (Teletex) */
FIO_DER_VIDEOTEX_STRING = 0x15, /**< Videotex String */
FIO_DER_IA5_STRING = 0x16, /**< IA5 String (ASCII) */
FIO_DER_UTC_TIME = 0x17, /**< UTC Time */
FIO_DER_GENERALIZED_TIME = 0x18, /**< Generalized Time */
FIO_DER_GRAPHIC_STRING = 0x19, /**< Graphic String */
FIO_DER_VISIBLE_STRING = 0x1A, /**< Visible String */
FIO_DER_GENERAL_STRING = 0x1B, /**< General String */
FIO_DER_UNIVERSAL_STRING = 0x1C, /**< Universal String */
FIO_DER_BMP_STRING = 0x1E, /**< BMP String (UCS-2) */
/* Context-specific tags (0x80 | tag_number) with constructed bit (0x20) */
FIO_DER_CONTEXT_0 = 0xA0, /**< [0] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_1 = 0xA1, /**< [1] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_2 = 0xA2, /**< [2] EXPLICIT/IMPLICIT */
FIO_DER_CONTEXT_3 = 0xA3, /**< [3] EXPLICIT/IMPLICIT */
} fio_der_tag_e
```
ASN.1 Universal Tag Types
_Symbol type:_ `type`
#### `fio_der_class_e`
```c
typedef enum {
FIO_DER_CLASS_UNIVERSAL = 0, /**< Universal (built-in types) */
FIO_DER_CLASS_APPLICATION = 1, /**< Application-specific */
FIO_DER_CLASS_CONTEXT = 2, /**< Context-specific */
FIO_DER_CLASS_PRIVATE = 3, /**< Private */
} fio_der_class_e
```
ASN.1 Tag Class (bits 7-6 of tag byte)
_Symbol type:_ `type`
#### `fio_der_element_s`
```c
typedef struct {
const uint8_t *data; /**< Pointer to element content (after tag+length) */
size_t len; /**< Length of content */
uint8_t tag; /**< Raw tag byte */
uint8_t is_constructed; /**< 1 if constructed (contains other elements) */
uint8_t tag_class; /**< 0=Universal, 1=Application, 2=Context, 3=Private */
uint8_t tag_number; /**< Tag number (bits 4-0, or extended) */
} fio_der_element_s
```
Parsed ASN.1 DER element
_Symbol type:_ `type`
#### `fio_der_iterator_s`
```c
typedef struct {
const uint8_t *pos; /**< Current position */
const uint8_t *end; /**< End of sequence */
} fio_der_iterator_s
```
Iterator for SEQUENCE or SET contents
_Symbol type:_ `type`
### Functions
#### `fio_der_parse`
```c
const uint8_t *fio_der_parse(fio_der_element_s *elem, const uint8_t *data, size_t data_len)
```
Parse one ASN.1 element from DER-encoded data.
**Parameters:**
- `elem` - Output structure to fill with parsed element info
- `data` - Pointer to DER-encoded data
- `data_len` - Length of data buffer
**Returns:**
- Pointer to next element (after this one), or NULL on error
_Symbol type:_ `function`
#### `fio_der_element_total_len`
```c
inline size_t fio_der_element_total_len(const fio_der_element_s *elem, const uint8_t *data)
```
Get the total encoded length of an ASN.1 element (tag + length + content).
**Parameters:**
- `elem` - Parsed element
- `data` - Original data pointer where element was parsed from
**Returns:**
- Total bytes used by the element encoding
_Symbol type:_ `function`
#### `fio_der_parse_integer`
```c
int fio_der_parse_integer(const fio_der_element_s *elem, uint64_t *value)
```
Parse an ASN.1 INTEGER element.
For small integers (<= 64-bit), sets *value.
For large integers (RSA modulus), use elem->data/len directly.
Leading zero bytes for positive numbers are handled correctly.
**Parameters:**
- `elem` - Parsed element (must be INTEGER type)
- `value` - Output for integer value (can be NULL for large integers)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_bit_string`
```c
int fio_der_parse_bit_string(const fio_der_element_s *elem, const uint8_t **bits, size_t *bit_len, uint8_t *unused_bits)
```
Parse an ASN.1 BIT STRING element.
**Parameters:**
- `elem` - Parsed element (must be BIT STRING type)
- `bits` - Output pointer to bit data (first byte is unused bits count)
- `bit_len` - Output length of bit data in bytes
- `unused_bits` - Output number of unused bits in last byte (0-7)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_oid`
```c
int fio_der_parse_oid(const fio_der_element_s *elem, char *buf, size_t buf_len)
```
Parse an ASN.1 OID into a dot-separated string.
Example output: "1.2.840.113549.1.1.11"
**Parameters:**
- `elem` - Parsed element (must be OID type)
- `buf` - Output buffer for string
- `buf_len` - Buffer size
**Returns:**
- Number of chars written (excluding NUL), or -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_time`
```c
int fio_der_parse_time(const fio_der_element_s *elem, int64_t *unix_time)
```
Parse an ASN.1 time (UTC Time or Generalized Time) to Unix timestamp.
**Parameters:**
- `elem` - Parsed element (must be UTC_TIME or GENERALIZED_TIME type)
- `unix_time` - Output Unix timestamp (seconds since 1970-01-01 00:00:00
UTC)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_parse_string`
```c
inline const char *fio_der_parse_string(const fio_der_element_s *elem, size_t *len)
```
Parse an ASN.1 string element.
Supports UTF8String, PrintableString, IA5String, etc.
Returns pointer directly into the element data (no copy).
**Parameters:**
- `elem` - Parsed element (must be a string type)
- `len` - Output length of string
**Returns:**
- Pointer to string data, or NULL on error
_Symbol type:_ `function`
#### `fio_der_parse_boolean`
```c
inline int fio_der_parse_boolean(const fio_der_element_s *elem, int *value)
```
Parse an ASN.1 BOOLEAN element.
**Parameters:**
- `elem` - Parsed element (must be BOOLEAN type)
- `value` - Output boolean value (0 = false, non-zero = true)
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_der_iterator_init`
```c
inline void fio_der_iterator_init(fio_der_iterator_s *it, const fio_der_element_s *sequence)
```
Initialize an iterator for a SEQUENCE or SET element.
**Parameters:**
- `it` - Iterator to initialize
- `sequence` - Parsed element (must be SEQUENCE or SET)
_Symbol type:_ `function`
#### `fio_der_iterator_next`
```c
int fio_der_iterator_next(fio_der_iterator_s *it, fio_der_element_s *elem)
```
Get the next element from an iterator.
**Parameters:**
- `it` - Iterator (updated to point to next element)
- `elem` - Output for parsed element
**Returns:**
- 0 if element available, -1 if end or error
_Symbol type:_ `function`
#### `fio_der_iterator_has_next`
```c
inline int fio_der_iterator_has_next(const fio_der_iterator_s *it)
```
Check if iterator has more elements.
**Parameters:**
- `it` - Iterator
**Returns:**
- 1 if more elements available, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_is_tag`
```c
inline int fio_der_is_tag(const fio_der_element_s *elem, uint8_t tag)
```
Check if an element is a specific tag type.
**Parameters:**
- `elem` - Parsed element
- `tag` - Expected tag (e.g., FIO_DER_INTEGER)
**Returns:**
- 1 if match, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_is_context_tag`
```c
inline int fio_der_is_context_tag(const fio_der_element_s *elem, uint8_t tag_num)
```
Check if an element is a context-specific tag.
**Parameters:**
- `elem` - Parsed element
- `tag_num` - Context tag number (0-31)
**Returns:**
- 1 if match, 0 otherwise
_Symbol type:_ `function`
#### `fio_der_tag_number`
```c
inline uint8_t fio_der_tag_number(const fio_der_element_s *elem)
```
Get the tag number from an element.
For universal tags, returns the tag value (0-30).
For context-specific tags, returns the context number.
**Parameters:**
- `elem` - Parsed element
**Returns:**
- Tag number
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/155 rsa.h`
9 public symbols.
### Macros
#### `FIO_RSA_MAX_BITS`
```c
#define FIO_RSA_MAX_BITS 4096
```
Maximum RSA key size in bits
_Symbol type:_ `macro`
#### `FIO_RSA_MAX_BYTES`
```c
#define FIO_RSA_MAX_BYTES (FIO_RSA_MAX_BITS / 8)
```
Maximum RSA key size in bytes
_Symbol type:_ `macro`
#### `FIO_RSA_MAX_WORDS`
```c
#define FIO_RSA_MAX_WORDS (FIO_RSA_MAX_BYTES / 8)
```
Maximum RSA key size in 64-bit words
_Symbol type:_ `macro`
### Types
#### `fio_rsa_hash_e`
```c
typedef enum {
FIO_RSA_HASH_SHA256 = 0, /**< SHA-256 (32 bytes) */
FIO_RSA_HASH_SHA384 = 1, /**< SHA-384 (48 bytes) */
FIO_RSA_HASH_SHA512 = 2, /**< SHA-512 (64 bytes) */
} fio_rsa_hash_e
```
Hash algorithm identifiers for RSA verification
_Symbol type:_ `type`
#### `fio_rsa_pubkey_s`
```c
typedef struct {
const uint8_t *n; /**< Modulus (big-endian) */
size_t n_len; /**< Modulus length in bytes */
const uint8_t *e; /**< Public exponent (big-endian) */
size_t e_len; /**< Exponent length in bytes */
} fio_rsa_pubkey_s
```
RSA public key for signature verification.
The modulus (n) and exponent (e) are stored as big-endian byte arrays.
This matches the DER encoding used in X.509 certificates.
_Symbol type:_ `type`
#### `fio_rsa_privkey_s`
```c
typedef struct {
const uint8_t *n; /**< Modulus (big-endian) */
size_t n_len; /**< Modulus length in bytes (256, 384, or 512) */
const uint8_t *d; /**< Private exponent (big-endian) */
size_t d_len; /**< Private exponent length in bytes */
const uint8_t *e; /**< Public exponent (big-endian), optional, for blinding */
size_t e_len;
const uint8_t *p; /**< Prime p (big-endian), optional, for CRT */
size_t p_len;
const uint8_t *q; /**< Prime q (big-endian), optional, for CRT */
size_t q_len;
const uint8_t *dP; /**< d mod (p-1) (big-endian), optional, for CRT */
size_t dP_len;
const uint8_t *dQ; /**< d mod (q-1) (big-endian), optional, for CRT */
size_t dQ_len;
const uint8_t *qInv; /**< q^-1 mod p (big-endian), optional, for CRT */
size_t qInv_len;
} fio_rsa_privkey_s
```
RSA private key for signature generation.
The modulus (n) and private exponent (d) are stored as big-endian byte
arrays. This matches the DER encoding used in PKCS#8 private keys.
Optional CRT parameters (p, q, dP, dQ, qInv) and the public exponent (e)
may be provided. When CRT parameters are available, signing uses CRT with
message blinding for better side-channel resistance. When only n and d are
available, signing falls back to a non-CRT (still constant-time) path.
All optional fields are indicated by a non-zero length. Missing CRT
parameters may be derived from p, q, and d when p and q are present.
_Symbol type:_ `type`
### Functions
#### `fio_rsa_verify_pkcs1`
```c
int fio_rsa_verify_pkcs1(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_pubkey_s *key)
```
Verify an RSA PKCS#1 v1.5 signature.
This verifies signatures with DigestInfo encoding as used in:
- sha256WithRSAEncryption (OID 1.2.840.113549.1.1.11)
- sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12)
- sha512WithRSAEncryption (OID 1.2.840.113549.1.1.13)
**Parameters:**
- `sig` - Signature bytes (same length as modulus)
- `sig_len` - Signature length in bytes
- `msg_hash` - Pre-computed hash of the message
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm used (FIO_RSA_HASH_SHA256, etc.)
- `key` - RSA public key
**Returns:**
- 0 on success (valid signature), -1 on failure
_Symbol type:_ `function`
#### `fio_rsa_verify_pss`
```c
int fio_rsa_verify_pss(const uint8_t *sig, size_t sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_pubkey_s *key)
```
Verify an RSA-PSS signature (required for TLS 1.3).
RSA-PSS uses probabilistic padding and is the mandatory signature scheme
for TLS 1.3 CertificateVerify messages with RSA keys.
This implementation uses:
- MGF1 with the same hash function
- Salt length = hash length (as required by TLS 1.3)
- Trailer field = 0xBC
**Parameters:**
- `sig` - Signature bytes (same length as modulus)
- `sig_len` - Signature length in bytes
- `msg_hash` - Pre-computed hash of the message
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm used
- `key` - RSA public key
**Returns:**
- 0 on success (valid signature), -1 on failure
_Symbol type:_ `function`
#### `fio_rsa_sign_pss`
```c
int fio_rsa_sign_pss(uint8_t *signature, size_t *sig_len, const uint8_t *msg_hash, size_t hash_len, fio_rsa_hash_e hash_alg, const fio_rsa_privkey_s *key)
```
Generate an RSA-PSS signature (RSASSA-PSS-SIGN per RFC 8017 Section 8.1.1).
This is required for TLS 1.3 server CertificateVerify messages when using
RSA certificates. PKCS#1 v1.5 signatures are NOT allowed for
CertificateVerify in TLS 1.3.
This implementation uses:
- MGF1 with the same hash function
- Salt length = hash length (as required by TLS 1.3)
- Trailer field = 0xBC
**Parameters:**
- `signature` - Output buffer for signature (must be key->n_len bytes)
- `sig_len` - Output: actual signature length (equals key->n_len)
- `msg_hash` - Pre-computed hash of the message to sign
- `hash_len` - Hash length (32, 48, or 64 bytes)
- `hash_alg` - Hash algorithm (FIO_RSA_HASH_SHA256, etc.)
- `key` - RSA private key
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/156 mlkem.h`
17 public symbols.
### Macros
#### `FIO_MLKEM768_PUBLICKEYBYTES`
```c
#define FIO_MLKEM768_PUBLICKEYBYTES 1184
```
ML-KEM-768 constants
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SECRETKEYBYTES`
```c
#define FIO_MLKEM768_SECRETKEYBYTES 2400
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_CIPHERTEXTBYTES`
```c
#define FIO_MLKEM768_CIPHERTEXTBYTES 1088
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SSBYTES`
```c
#define FIO_MLKEM768_SSBYTES 32
```
_Symbol type:_ `macro`
#### `FIO_MLKEM768_SYMBYTES`
```c
#define FIO_MLKEM768_SYMBYTES 32
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_PUBLICKEYBYTES`
```c
#define FIO_X25519MLKEM768_PUBLICKEYBYTES (32 + 1184) /* 1216 */
```
X25519MLKEM768 hybrid constants
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_SECRETKEYBYTES`
```c
#define FIO_X25519MLKEM768_SECRETKEYBYTES (32 + 2400) /* 2432 */
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_CIPHERTEXTBYTES`
```c
#define FIO_X25519MLKEM768_CIPHERTEXTBYTES (32 + 1088) /* 1120 */
```
_Symbol type:_ `macro`
#### `FIO_X25519MLKEM768_SSBYTES`
```c
#define FIO_X25519MLKEM768_SSBYTES 64
```
_Symbol type:_ `macro`
### Functions
#### `fio_mlkem768_keypair`
```c
int fio_mlkem768_keypair(uint8_t pk[1184], uint8_t sk[2400])
```
Generate ML-KEM-768 keypair.
Generates a random keypair using the system CSPRNG.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_keypair_derand`
```c
int fio_mlkem768_keypair_derand(uint8_t pk[1184], uint8_t sk[2400], const uint8_t coins[64])
```
Generate ML-KEM-768 keypair from deterministic seed.
The coins buffer must be exactly 64 bytes (d || z).
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_encaps`
```c
int fio_mlkem768_encaps(uint8_t ct[1088], uint8_t ss[32], const uint8_t pk[1184])
```
Encapsulate: generate ciphertext and shared secret from public key.
Uses system CSPRNG for randomness.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_encaps_derand`
```c
int fio_mlkem768_encaps_derand(uint8_t ct[1088], uint8_t ss[32], const uint8_t pk[1184], const uint8_t coins[32])
```
Encapsulate with deterministic randomness.
The coins buffer must be exactly 32 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_mlkem768_decaps`
```c
int fio_mlkem768_decaps(uint8_t ss[32], const uint8_t ct[1088], const uint8_t sk[2400])
```
Decapsulate: recover shared secret from ciphertext and secret key.
Uses implicit rejection: if the ciphertext is invalid, a pseudorandom
shared secret is returned (derived from the secret key and ciphertext)
rather than an error, preventing chosen-ciphertext attacks.
Returns 0 on success (always succeeds for well-formed inputs).
_Symbol type:_ `function`
#### `fio_x25519mlkem768_keypair`
```c
int fio_x25519mlkem768_keypair(uint8_t pk[1216], uint8_t sk[2432])
```
Generate X25519MLKEM768 hybrid keypair.
Generates both X25519 and ML-KEM-768 keypairs using system CSPRNG.
The public key is ML-KEM-768_ek (1184) || X25519_pk (32) = 1216 bytes.
The secret key is ML-KEM-768_dk (2400) || X25519_sk (32) = 2432 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_x25519mlkem768_encaps`
```c
int fio_x25519mlkem768_encaps(uint8_t ct[1120], uint8_t ss[64], const uint8_t pk[1216])
```
X25519MLKEM768 hybrid encapsulation.
Performs both X25519 key exchange and ML-KEM-768 encapsulation.
The ciphertext is ML-KEM-768_ct (1088) || X25519_ephemeral_pk (32) = 1120
bytes. The shared secret is ML-KEM-768_ss (32) || X25519_ss (32) = 64 bytes.
Returns 0 on success, -1 on failure.
_Symbol type:_ `function`
#### `fio_x25519mlkem768_decaps`
```c
int fio_x25519mlkem768_decaps(uint8_t ss[64], const uint8_t ct[1120], const uint8_t sk[2432])
```
X25519MLKEM768 hybrid decapsulation.
Performs both X25519 shared secret derivation and ML-KEM-768 decapsulation.
The shared secret is ML-KEM-768_ss (32) || X25519_ss (32) = 64 bytes.
Returns 0 on success, -1 if X25519 shared secret computation fails
(low-order point). ML-KEM-768 uses implicit rejection for invalid
ciphertexts.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/156 x509.h`
57 public symbols.
### Macros
#### `FIO_X509_OID_SHA256_WITH_RSA`
```c
#define FIO_X509_OID_SHA256_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, [15] = 9}}) /* 1.2.840.113549.1.1.11 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SHA384_WITH_RSA`
```c
#define FIO_X509_OID_SHA384_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, [15] = 9}}) /* 1.2.840.113549.1.1.12 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SHA512_WITH_RSA`
```c
#define FIO_X509_OID_SHA512_WITH_RSA \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, [15] = 9}}) /* 1.2.840.113549.1.1.13 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_RSA_PSS`
```c
#define FIO_X509_OID_RSA_PSS \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A, [15] = 9}}) /* 1.2.840.113549.1.1.10 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA256`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA256 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, [15] = 8}}) /* 1.2.840.10045.4.3.2 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA384`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA384 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, [15] = 8}}) /* 1.2.840.10045.4.3.3 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ECDSA_WITH_SHA512`
```c
#define FIO_X509_OID_ECDSA_WITH_SHA512 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, [15] = 8}}) /* 1.2.840.10045.4.3.4 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ED25519`
```c
#define FIO_X509_OID_ED25519 \
((fio_u128){.u8 = {0x2B, 0x65, 0x70, [15] = 3}}) /* 1.3.101.112 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ED448`
```c
#define FIO_X509_OID_ED448 \
((fio_u128){.u8 = {0x2B, 0x65, 0x71, [15] = 3}}) /* 1.3.101.113 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_RSA_ENCRYPTION`
```c
#define FIO_X509_OID_RSA_ENCRYPTION \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, [15] = 9}}) /* 1.2.840.113549.1.1.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EC_PUBLIC_KEY`
```c
#define FIO_X509_OID_EC_PUBLIC_KEY \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, [15] = 7}}) /* 1.2.840.10045.2.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP256R1`
```c
#define FIO_X509_OID_SECP256R1 \
((fio_u128){.u8 = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, [15] = 8}}) /* 1.2.840.10045.3.1.7 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP384R1`
```c
#define FIO_X509_OID_SECP384R1 \
((fio_u128){.u8 = {0x2B, 0x81, 0x04, 0x00, 0x22, [15] = 5}}) /* 1.3.132.0.34 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SECP521R1`
```c
#define FIO_X509_OID_SECP521R1 \
((fio_u128){.u8 = {0x2B, 0x81, 0x04, 0x00, 0x23, [15] = 5}}) /* 1.3.132.0.35 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_X25519`
```c
#define FIO_X509_OID_X25519 \
((fio_u128){.u8 = {0x2B, 0x65, 0x6E, [15] = 3}}) /* 1.3.101.110 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_X448`
```c
#define FIO_X509_OID_X448 \
((fio_u128){.u8 = {0x2B, 0x65, 0x6F, [15] = 3}}) /* 1.3.101.111 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SUBJECT_KEY_ID`
```c
#define FIO_X509_OID_SUBJECT_KEY_ID \
((fio_u128){.u8 = {0x55, 0x1D, 0x0E, [15] = 3}}) /* 2.5.29.14 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_KEY_USAGE`
```c
#define FIO_X509_OID_KEY_USAGE \
((fio_u128){.u8 = {0x55, 0x1D, 0x0F, [15] = 3}}) /* 2.5.29.15 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_SUBJECT_ALT_NAME`
```c
#define FIO_X509_OID_SUBJECT_ALT_NAME \
((fio_u128){.u8 = {0x55, 0x1D, 0x11, [15] = 3}}) /* 2.5.29.17 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_BASIC_CONSTRAINTS`
```c
#define FIO_X509_OID_BASIC_CONSTRAINTS \
((fio_u128){.u8 = {0x55, 0x1D, 0x13, [15] = 3}}) /* 2.5.29.19 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_CRL_DIST_POINTS`
```c
#define FIO_X509_OID_CRL_DIST_POINTS \
((fio_u128){.u8 = {0x55, 0x1D, 0x1F, [15] = 3}}) /* 2.5.29.31 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_CERT_POLICIES`
```c
#define FIO_X509_OID_CERT_POLICIES \
((fio_u128){.u8 = {0x55, 0x1D, 0x20, [15] = 3}}) /* 2.5.29.32 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_AUTH_KEY_ID`
```c
#define FIO_X509_OID_AUTH_KEY_ID \
((fio_u128){.u8 = {0x55, 0x1D, 0x23, [15] = 3}}) /* 2.5.29.35 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EXT_KEY_USAGE`
```c
#define FIO_X509_OID_EXT_KEY_USAGE \
((fio_u128){.u8 = {0x55, 0x1D, 0x25, [15] = 3}}) /* 2.5.29.37 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EKU_SERVER_AUTH`
```c
#define FIO_X509_OID_EKU_SERVER_AUTH \
((fio_u128){.u8 = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, [15] = 8}}) /* 1.3.6.1.5.5.7.3.1 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_EKU_CLIENT_AUTH`
```c
#define FIO_X509_OID_EKU_CLIENT_AUTH \
((fio_u128){.u8 = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, [15] = 8}}) /* 1.3.6.1.5.5.7.3.2 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_COMMON_NAME`
```c
#define FIO_X509_OID_COMMON_NAME \
((fio_u128){.u8 = {0x55, 0x04, 0x03, [15] = 3}}) /* 2.5.4.3 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_COUNTRY`
```c
#define FIO_X509_OID_COUNTRY \
((fio_u128){.u8 = {0x55, 0x04, 0x06, [15] = 3}}) /* 2.5.4.6 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_LOCALITY`
```c
#define FIO_X509_OID_LOCALITY \
((fio_u128){.u8 = {0x55, 0x04, 0x07, [15] = 3}}) /* 2.5.4.7 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_STATE`
```c
#define FIO_X509_OID_STATE \
((fio_u128){.u8 = {0x55, 0x04, 0x08, [15] = 3}}) /* 2.5.4.8 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ORGANIZATION`
```c
#define FIO_X509_OID_ORGANIZATION \
((fio_u128){.u8 = {0x55, 0x04, 0x0A, [15] = 3}}) /* 2.5.4.10 */
```
_Symbol type:_ `macro`
#### `FIO_X509_OID_ORG_UNIT`
```c
#define FIO_X509_OID_ORG_UNIT \
((fio_u128){.u8 = {0x55, 0x04, 0x0B, [15] = 3}}) /* 2.5.4.11 */
```
_Symbol type:_ `macro`
#### `FIO_TLS_CERT_PARSE_ERROR`
```c
#define FIO_TLS_CERT_PARSE_ERROR ((size_t)-1)
```
Error value for fio_tls_parse_certificate_message
_Symbol type:_ `macro`
#### `FIO_X509_MAX_CHAIN_DEPTH`
```c
#define FIO_X509_MAX_CHAIN_DEPTH 10
```
_Symbol type:_ `macro`
### Types
#### `fio_x509_key_algo_e`
```c
typedef enum {
FIO_X509_KEY_UNKNOWN = 0,
FIO_X509_KEY_RSA = 1, /**< RSA (any key size) */
FIO_X509_KEY_ECDSA_P256 = 2, /**< ECDSA with P-256/secp256r1 */
FIO_X509_KEY_ECDSA_P384 = 3, /**< ECDSA with P-384/secp384r1 */
FIO_X509_KEY_ED25519 = 4, /**< Ed25519 (EdDSA) */
} fio_x509_key_algo_e
```
Public key algorithm types
_Symbol type:_ `type`
#### `fio_x509_signature_algo_e`
```c
typedef enum {
FIO_X509_SIGNATURE_UNKNOWN = 0,
FIO_X509_SIGNATURE_RSA_PKCS1_SHA256 = 1, /**< sha256WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PKCS1_SHA384 = 2, /**< sha384WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PKCS1_SHA512 = 3, /**< sha512WithRSAEncryption */
FIO_X509_SIGNATURE_RSA_PSS_SHA256 = 4, /**< RSA-PSS with SHA-256 */
FIO_X509_SIGNATURE_RSA_PSS_SHA384 = 5, /**< RSA-PSS with SHA-384 */
FIO_X509_SIGNATURE_RSA_PSS_SHA512 = 6, /**< RSA-PSS with SHA-512 */
FIO_X509_SIGNATURE_ECDSA_SHA256 = 7, /**< ecdsa-with-SHA256 */
FIO_X509_SIGNATURE_ECDSA_SHA384 = 8, /**< ecdsa-with-SHA384 */
FIO_X509_SIGNATURE_ED25519 = 9, /**< Ed25519 */
} fio_x509_signature_algo_e
```
Signature algorithm types
_Symbol type:_ `type`
#### `fio_x509_key_usage_e`
```c
typedef enum {
FIO_X509_KU_DIGITAL_SIGNATURE = 0x0080, /* bit 0 = MSB of byte 0 */
FIO_X509_KU_NON_REPUDIATION = 0x0040, /* bit 1 */
FIO_X509_KU_KEY_ENCIPHERMENT = 0x0020, /* bit 2 */
FIO_X509_KU_DATA_ENCIPHERMENT = 0x0010, /* bit 3 */
FIO_X509_KU_KEY_AGREEMENT = 0x0008, /* bit 4 */
FIO_X509_KU_KEY_CERT_SIGN = 0x0004, /* bit 5 */
FIO_X509_KU_CRL_SIGN = 0x0002, /* bit 6 */
FIO_X509_KU_ENCIPHER_ONLY = 0x0001, /* bit 7 */
FIO_X509_KU_DECIPHER_ONLY = 0x8000, /* bit 8 = MSB of byte 1 */
} fio_x509_key_usage_e
```
Key Usage bit flags (RFC 5280 Section 4.2.1.3)
ASN.1 BIT STRING uses MSB-first bit ordering:
- Bit 0 = MSB of first byte (0x80)
- Bit 1 = 0x40, Bit 2 = 0x20, etc.
- Bits 8+ are in the second byte
_Symbol type:_ `type`
#### `fio_x509_error_e`
```c
typedef enum {
FIO_X509_OK = 0, /**< Validation successful */
FIO_X509_ERR_PARSE = -1, /**< Failed to parse certificate */
FIO_X509_ERR_EXPIRED = -2, /**< Certificate expired */
FIO_X509_ERR_NOT_YET_VALID = -3, /**< Certificate not yet valid */
FIO_X509_ERR_SIGNATURE = -4, /**< Signature verification failed */
FIO_X509_ERR_ISSUER_MISMATCH = -5, /**< Issuer DN doesn't match subject DN */
FIO_X509_ERR_NOT_CA = -6, /**< Issuer is not a CA certificate */
FIO_X509_ERR_NO_TRUST_ANCHOR = -7, /**< Certificate not in trust store */
FIO_X509_ERR_HOSTNAME_MISMATCH = -8, /**< Hostname doesn't match cert */
FIO_X509_ERR_EMPTY_CHAIN = -9, /**< Empty certificate chain */
FIO_X509_ERR_CHAIN_TOO_LONG = -10, /**< Chain exceeds maximum depth */
} fio_x509_error_e
```
X.509 chain validation error codes
_Symbol type:_ `type`
#### `fio_x509_trust_store_s`
```c
typedef struct {
const uint8_t **roots; /**< Array of root CA certificate DER data */
const size_t *root_lens; /**< Array of root CA certificate lengths */
size_t root_count; /**< Number of root CAs */
} fio_x509_trust_store_s
```
Trust store for root CA certificates
_Symbol type:_ `type`
#### `fio_tls_cert_entry_s`
```c
typedef struct {
const uint8_t *cert; /**< DER-encoded certificate data */
size_t cert_len; /**< Certificate length */
} fio_tls_cert_entry_s
```
TLS certificate entry (parsed from Certificate message)
_Symbol type:_ `type`
#### `fio_x509_cert_s`
```c
struct fio_x509_cert_s {
/* Buffer views (16 bytes each, 8-byte aligned — no padding) */
/** Raw DER data (points to the original input, NOT a copy) */
fio_ubuf_info_s der;
/** Certificate serial number (raw INTEGER contents, pointer into DER) */
fio_ubuf_info_s serial;
/** Subject Distinguished Name (raw DER for comparison) */
fio_ubuf_info_s subject;
/** Issuer Distinguished Name (raw DER for comparison) */
fio_ubuf_info_s issuer;
/** Subject Common Name (if present, pointer into DER data) */
fio_buf_info_s cn;
/** Public Key Data (union based on key_algo) */
union {
struct {
fio_ubuf_info_s n; /**< RSA modulus (big-endian) */
fio_ubuf_info_s e; /**< RSA exponent (big-endian) */
} rsa;
struct {
fio_ubuf_info_s point; /**< Uncompressed EC point (04 || x || y) */
} ecdsa;
struct {
fio_ubuf_info_s key; /**< 32-byte Ed25519 public key */
} ed25519;
} pubkey;
/** Signature value (pointer into DER data) */
fio_ubuf_info_s signature;
/** TBS Certificate (for signature verification) */
fio_ubuf_info_s tbs;
/** Subject Alternative Name: first DNS name (if present) */
fio_buf_info_s san_dns;
/** Subject Alternative Name extension raw data (for iterating all SANs) */
fio_ubuf_info_s san_ext;
/** Subject Alternative Name: first IP address (if present) */
fio_ubuf_info_s san_ip;
/** Validity period (Unix timestamps) */
int64_t not_before;
int64_t not_after;
/** SHA-256 fingerprint of the DER data (see fio_x509_fingerprint) */
uint8_t fingerprint[32];
/* Small fields grouped at the end (no bitfields — byte access is faster) */
/** Public Key Type */
fio_x509_key_algo_e key_algo;
/** Signature Algorithm */
fio_x509_signature_algo_e signature_algo;
/** Key Usage extension bits */
uint16_t key_usage;
/** Peer chain verification state: non-zero if a TLS backend verified this
* certificate's chain (see fio_io_peer_info_next). Always zero after
* fio_x509_parse — parsing does not verify anything. */
uint8_t verified;
/** Position in the peer certificate chain (0 = leaf certificate).
* Iteration is capped at 128 certificates (max value 127) as a
* deep-nesting / DoS guard. Set by fio_io_peer_info_next.
* Always zero after fio_x509_parse. */
uint8_t chain_index;
/** Certificate version (0=v1, 1=v2, 2=v3) */
uint8_t version;
/** Basic Constraints: is CA */
uint8_t is_ca;
/** Key Usage extension present */
uint8_t has_key_usage;
}
```
Parsed X.509 certificate structure.
All buffer fields are non-owning views (`fio_buf_info_s` /
`fio_ubuf_info_s`) into the original DER data, which must remain valid
while the certificate is in use.
When a certificate is obtained through fio_io_peer_info_next (TLS peer
certificate inspection), the views point into memory owned by the TLS
backend and remain valid until the next fio_io_peer_info_next call (on ANY
connection) or until the connection is closed, whichever comes first.
_Symbol type:_ `type`
#### `fio_x509_keypair_type_e`
```c
typedef enum {
FIO_X509_KEYPAIR_ED25519 = 1, /**< Ed25519 (preferred) */
FIO_X509_KEYPAIR_P256 = 2, /**< ECDSA P-256 */
} fio_x509_keypair_type_e
```
Key pair types for certificate generation
_Symbol type:_ `type`
#### `fio_x509_keypair_s`
```c
typedef struct {
fio_x509_keypair_type_e type; /**< Key type */
uint8_t secret_key[64]; /**< Secret key (32 bytes for Ed25519, P-256) */
uint8_t public_key[65]; /**< Public key (32 for Ed25519, 65 for P-256) */
size_t secret_key_len; /**< Actual secret key length */
size_t public_key_len; /**< Actual public key length */
} fio_x509_keypair_s
```
Key pair structure for certificate generation
_Symbol type:_ `type`
#### `fio_x509_cert_options_s`
```c
typedef struct {
fio_buf_info_s cn; /**< Subject Common Name (required) */
fio_buf_info_s org; /**< Subject Organization (optional) */
fio_buf_info_s ou; /**< Subject Organizational Unit (optional) */
fio_buf_info_s country; /**< Subject Country (optional, 2 chars) */
int64_t not_before; /**< Validity start (Unix timestamp, 0 = now) */
int64_t not_after; /**< Validity end (Unix timestamp, 0 = +1 year) */
const fio_buf_info_s *san_dns; /**< Subject Alternative Names (DNS) */
size_t san_dns_count; /**< Number of SAN DNS entries */
int is_ca; /**< Set CA:TRUE in BasicConstraints */
uint16_t key_usage; /**< Key Usage bits (0 = default for type) */
} fio_x509_cert_options_s
```
Certificate generation options.
All string fields are `fio_buf_info_s` views (non-owning) — a zero `.len`
marks the field as absent. There is NO implicit strlen fallback: callers
wrapping C strings should use `FIO_BUF_INFO2((char *)s, FIO_STRLEN(s))`.
_Symbol type:_ `type`
### Functions
#### `fio_x509_parse`
```c
int fio_x509_parse(fio_x509_cert_s *cert, const uint8_t *der_data, size_t der_len)
```
Parse a DER-encoded X.509 certificate.
The cert structure will contain pointers into the original DER data,
so the DER data must remain valid while the cert is in use.
**Parameters:**
- `cert` - Output certificate structure (will be zeroed first)
- `der_data` - Pointer to DER-encoded certificate
- `der_len` - Length of DER data in bytes
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_fingerprint`
```c
void fio_x509_fingerprint(fio_x509_cert_s *cert)
```
Computes the SHA-256 fingerprint of the certificate's DER data, storing
the raw 32-byte hash in `cert->fingerprint`.
Call after fio_x509_parse. This is a lazy (on-demand) operation so that
parsing alone never pays the hashing cost.
**Parameters:**
- `cert` - Parsed certificate (must have been filled by fio_x509_parse)
_Symbol type:_ `function`
#### `fio_x509_verify_signature`
```c
int fio_x509_verify_signature(const fio_x509_cert_s *cert, const fio_x509_cert_s *issuer)
```
Verify certificate signature using issuer's public key.
This verifies that the certificate was signed by the issuer.
**Parameters:**
- `cert` - Certificate to verify
- `issuer` - Certificate of the issuer (contains the public key)
**Returns:**
- 0 if valid, -1 if invalid or error
_Symbol type:_ `function`
#### `fio_x509_check_validity`
```c
inline int fio_x509_check_validity(const fio_x509_cert_s *cert, int64_t current_time)
```
Check if certificate is currently valid (not expired, not yet valid).
**Parameters:**
- `cert` - Certificate to check
- `current_time` - Current Unix timestamp (seconds since epoch)
**Returns:**
- 0 if valid, -1 if expired or not yet valid
_Symbol type:_ `function`
#### `fio_x509_match_hostname`
```c
int fio_x509_match_hostname(const fio_x509_cert_s *cert, const char *hostname, size_t hostname_len)
```
Check if hostname matches certificate (CN or SAN).
Supports wildcard matching (*.example.com).
Per RFC 6125, wildcards only match one label.
**Parameters:**
- `cert` - Certificate to check
- `hostname` - Hostname to match
- `hostname_len` - Length of hostname
**Returns:**
- 0 if match, -1 if no match
_Symbol type:_ `function`
#### `fio_x509_verify_chain`
```c
int fio_x509_verify_chain(const fio_ubuf_info_s *certs, size_t cert_count, const char *hostname, int64_t current_time, fio_x509_trust_store_s *trust_store)
```
Validate a certificate chain for TLS 1.3.
The chain should be ordered from end-entity to closest-to-root:
- certs[0] = server's certificate (end-entity)
- certs[1] = intermediate CA (signed certs[0])
- certs[n-1] = closest to root (may be root or intermediate)
Validation performs:
1. Parse all certificates
2. Check validity period for all certificates
3. Verify hostname matches end-entity certificate (if hostname provided)
4. Verify each certificate's signature using the next certificate's key
5. Verify issuer DNs match subject DNs in the chain
6. Verify intermediate/root certificates have CA:TRUE
7. Verify the chain terminates at a trusted root (if trust store provided)
**Parameters:**
- `certs` - Array of DER-encoded certificate views
- `cert_count` - Number of certificates in chain
- `hostname` - Expected hostname for end-entity (NULL to skip check)
- `current_time` - Current Unix timestamp for validity checking
- `trust_store` - Root CA certificates (NULL to skip trust check)
**Returns:**
- FIO_X509_OK (0) on success, or error code on failure
_Symbol type:_ `function`
#### `fio_x509_is_trusted`
```c
int fio_x509_is_trusted(const fio_x509_cert_s *cert, fio_x509_trust_store_s *trust_store)
```
Check if a certificate is in the trust store.
Comparison is done by matching subject DN.
**Parameters:**
- `cert` - Certificate to check
- `trust_store` - Trust store to search
**Returns:**
- 0 if trusted, -1 if not found
_Symbol type:_ `function`
#### `fio_tls_parse_certificate_message`
```c
size_t fio_tls_parse_certificate_message(fio_tls_cert_entry_s *entries, size_t max_entries, const uint8_t *data, size_t data_len)
```
Parse TLS 1.3 Certificate message into individual certificates.
TLS 1.3 Certificate message format (RFC 8446):
certificate_request_context<0..2^8-1>
certificate_list<0..2^24-1>:
CertificateEntry:
cert_data<1..2^24-1>
extensions<0..2^16-1>
**Parameters:**
- `entries` - Output array for certificate entries
- `max_entries` - Maximum entries to parse
- `data` - Raw Certificate message data (after handshake header)
- `data_len` - Length of Certificate message data
**Returns:**
- Number of certificates parsed, or FIO_TLS_CERT_PARSE_ERROR on error
_Symbol type:_ `function`
#### `fio_x509_error_str`
```c
inline const char *fio_x509_error_str(int error)
```
Get human-readable error string for X.509 validation error code.
**Parameters:**
- `error` - Error code from fio_x509_verify_chain
**Returns:**
- Static string describing the error
_Symbol type:_ `function`
#### `fio_x509_keypair_ed25519`
```c
int fio_x509_keypair_ed25519(fio_x509_keypair_s *keypair)
```
Generate an Ed25519 key pair for certificate signing.
**Parameters:**
- `keypair` - Output key pair structure
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_keypair_p256`
```c
int fio_x509_keypair_p256(fio_x509_keypair_s *keypair)
```
Generate a P-256 key pair for certificate signing.
**Parameters:**
- `keypair` - Output key pair structure
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_x509_self_signed_cert`
```c
size_t fio_x509_self_signed_cert(uint8_t *buf, size_t buf_len, const fio_x509_keypair_s *keypair, const fio_x509_cert_options_s *options)
```
Generate a self-signed X.509v3 certificate.
The certificate is DER-encoded and written to the output buffer.
Call with buf=NULL to get the maximum possible certificate size.
This returns a worst-case size that accounts for variable-length DER
encoding (e.g., serial numbers with varying leading zeros), ensuring
that a buffer of this size will always be sufficient for the actual
generation call.
**Parameters:**
- `buf` - Output buffer (can be NULL to calculate max size)
- `buf_len` - Buffer size (ignored if buf is NULL)
- `keypair` - Key pair to use for signing
- `options` - Certificate options
**Returns:**
- Number of bytes written (if buf!=NULL) or max needed (if buf==NULL),
or 0 on error
_Symbol type:_ `function`
#### `fio_x509_keypair_clear`
```c
inline void fio_x509_keypair_clear(fio_x509_keypair_s *keypair)
```
Securely clear a key pair structure.
**Parameters:**
- `keypair` - Key pair to clear
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/157 pem.h`
8 public symbols.
### Types
#### `fio_pem_key_type_e`
```c
typedef enum {
FIO_PEM_KEY_UNKNOWN = 0,
FIO_PEM_KEY_RSA = 1,
FIO_PEM_KEY_ECDSA_P256 = 2,
FIO_PEM_KEY_ED25519 = 3,
} fio_pem_key_type_e
```
Private key algorithm types
_Symbol type:_ `type`
#### `fio_pem_s`
```c
typedef struct {
const uint8_t *der; /**< Pointer to decoded DER data */
size_t der_len; /**< Length of DER data */
const char *label; /**< PEM label (e.g., "CERTIFICATE", "PRIVATE KEY") */
size_t label_len; /**< Length of label */
} fio_pem_s
```
Parsed PEM block
_Symbol type:_ `type`
#### `fio_pem_private_key_s`
```c
typedef struct {
fio_pem_key_type_e type;
union {
struct {
uint8_t n[FIO_RSA_MAX_BYTES]; /**< RSA modulus (big-endian) */
size_t n_len;
uint8_t e[FIO_RSA_MAX_BYTES]; /**< RSA public exponent (big-endian) */
size_t e_len;
uint8_t d[FIO_RSA_MAX_BYTES]; /**< RSA private exponent (big-endian) */
size_t d_len;
uint8_t p[FIO_RSA_MAX_BYTES]; /**< RSA prime p (optional) */
size_t p_len;
uint8_t q[FIO_RSA_MAX_BYTES]; /**< RSA prime q (optional) */
size_t q_len;
uint8_t dP[FIO_RSA_MAX_BYTES]; /**< d mod (p-1) (optional) */
size_t dP_len;
uint8_t dQ[FIO_RSA_MAX_BYTES]; /**< d mod (q-1) (optional) */
size_t dQ_len;
uint8_t qInv[FIO_RSA_MAX_BYTES]; /**< q^-1 mod p (optional) */
size_t qInv_len;
} rsa;
struct {
uint8_t private_key[32]; /**< P-256 scalar (32 bytes) */
uint8_t public_key[65]; /**< Uncompressed point (optional, can derive) */
int has_public_key; /**< 1 if public_key is populated */
} ecdsa_p256;
struct {
uint8_t private_key[32]; /**< Ed25519 seed (32 bytes) */
uint8_t public_key[32]; /**< Ed25519 public key (optional) */
int has_public_key; /**< 1 if public_key is populated */
} ed25519;
};
} fio_pem_private_key_s
```
Parsed private key structure
_Symbol type:_ `type`
### Functions
#### `fio_pem_parse`
```c
size_t fio_pem_parse(fio_pem_s *out, uint8_t *der_buf, size_t der_buf_len, const char *pem_data, size_t pem_len)
```
Parse a single PEM block from data.
Finds the next -----BEGIN ----- and -----END ----- markers,
base64 decodes the content between them, and returns the DER data.
**Parameters:**
- `out` - Output structure to fill with parsed PEM block info
- `der_buf` - Buffer to store decoded DER data (caller-provided)
- `der_buf_len` - Size of der_buf
- `pem_data` - PEM-encoded data
- `pem_len` - Length of PEM data
**Returns:**
- Number of bytes consumed from pem_data, or 0 on error
_Symbol type:_ `function`
#### `fio_pem_parse_certificate`
```c
int fio_pem_parse_certificate(fio_x509_cert_s *cert, const char *pem_data, size_t pem_len)
```
Parse certificate from PEM file content.
Handles "CERTIFICATE" label and parses the X.509 certificate.
**Parameters:**
- `cert` - Output certificate structure (from fio_x509.h)
- `pem_data` - PEM-encoded certificate data
- `pem_len` - Length of PEM data
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_pem_parse_private_key`
```c
int fio_pem_parse_private_key(fio_pem_private_key_s *key, const char *pem_data, size_t pem_len)
```
Parse private key from PEM file content.
Supports:
- "PRIVATE KEY" (PKCS#8 PrivateKeyInfo)
- "RSA PRIVATE KEY" (PKCS#1 RSAPrivateKey)
- "EC PRIVATE KEY" (SEC1 ECPrivateKey)
**Parameters:**
- `key` - Output private key structure
- `pem_data` - PEM-encoded private key data
- `pem_len` - Length of PEM data
**Returns:**
- 0 on success, -1 on error
_Symbol type:_ `function`
#### `fio_pem_get_certificate_der`
```c
size_t fio_pem_get_certificate_der(uint8_t *der_out, size_t der_out_len, const char *pem_data, size_t pem_len)
```
Get the DER-encoded certificate from PEM data.
This is a convenience function that extracts just the DER bytes
without parsing the X.509 structure.
**Parameters:**
- `der_out` - Output buffer for DER data
- `der_out_len` - Size of output buffer
- `pem_data` - PEM-encoded certificate data
- `pem_len` - Length of PEM data
**Returns:**
- Length of DER data written, or 0 on error
_Symbol type:_ `function`
#### `fio_pem_private_key_clear`
```c
inline void fio_pem_private_key_clear(fio_pem_private_key_s *key)
```
Securely clear a private key structure.
**Parameters:**
- `key` - Private key to clear
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/159 argon2.h`
6 public symbols.
### Types
#### `fio_argon2_type_e`
```c
typedef enum {
FIO_ARGON2D = 0,
FIO_ARGON2I = 1,
FIO_ARGON2ID = 2,
} fio_argon2_type_e
```
Argon2 type selector.
_Symbol type:_ `type`
#### `fio_argon2_args_s`
```c
typedef struct {
/** The password (message P). */
fio_buf_info_s password;
/** The salt (nonce S). */
fio_buf_info_s salt;
/** Optional secret key K. */
fio_buf_info_s secret;
/** Optional associated data X. */
fio_buf_info_s ad;
/** Time cost (number of passes t, minimum 1). */
uint32_t t_cost;
/** Memory cost in KiB (minimum 8*parallelism). */
uint32_t m_cost;
/** Degree of parallelism (number of lanes, minimum 1). */
uint32_t parallelism;
/** Desired output (tag) length in bytes (minimum 4, default 32). */
uint32_t outlen;
/** Argon2 variant: FIO_ARGON2D, FIO_ARGON2I, or FIO_ARGON2ID. */
fio_argon2_type_e type;
} fio_argon2_args_s
```
Argon2 named arguments.
_Symbol type:_ `type`
### Functions
#### `fio_argon2`
```c
fio_u512 fio_argon2(fio_argon2_args_s args)
```
Computes Argon2 password hash (RFC 9106).
Supports Argon2d, Argon2i, and Argon2id variants.
Output is written to the returned buffer (up to 64 bytes via fio_u512).
For outlen > 64, use fio_argon2_hash() instead.
Use `fio_ct_is_eq` to compare output hashes in constant time.
_Symbol type:_ `function`
#### `fio_argon2`
```c
#define fio_argon2(...) fio_argon2((fio_argon2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_argon2_hash`
```c
int fio_argon2_hash(void *out, fio_argon2_args_s args)
```
Computes Argon2 password hash into a caller-provided buffer.
Supports arbitrary output lengths (minimum 4 bytes).
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_argon2_hash`
```c
#define fio_argon2_hash(out, ...) \
fio_argon2_hash(out, (fio_argon2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 lyra2.h`
5 public symbols.
### Types
#### `fio_lyra2_args_s`
```c
typedef struct {
/** The password to hash. */
fio_buf_info_s password;
/** The salt for the hash. */
fio_buf_info_s salt;
/** Time cost (number of rounds, minimum 1). */
uint64_t t_cost;
/** Memory cost (number of rows in the matrix, minimum 3). */
uint64_t m_cost;
/** Desired output length in bytes (default 32 if 0). */
size_t outlen;
/** Number of columns (default 256 if 0). */
size_t n_cols;
} fio_lyra2_args_s
```
Lyra2 named arguments.
_Symbol type:_ `type`
### Functions
#### `fio_lyra2`
```c
fio_u512 fio_lyra2(fio_lyra2_args_s args)
```
Computes Lyra2 password hash.
Uses Blake2b as the underlying sponge/hash function.
Matches the reference C implementation (nPARALLEL==1, SPONGE==0, RHO==1).
Output is written to the returned buffer (up to 512 bytes via fio_u512).
For outlen > 64, use fio_lyra2_hash() instead.
Use `fio_ct_is_eq` to compare output hashes in constant time.
_Symbol type:_ `function`
#### `fio_lyra2`
```c
#define fio_lyra2(...) fio_lyra2((fio_lyra2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_lyra2_hash`
```c
int fio_lyra2_hash(void *out, fio_lyra2_args_s args)
```
Computes Lyra2 password hash into a caller-provided buffer.
Supports arbitrary output lengths.
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_lyra2_hash`
```c
#define fio_lyra2_hash(out, ...) \
fio_lyra2_hash(out, (fio_lyra2_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 otp.h`
7 public symbols.
### Types
#### `fio_otp_settings_s`
```c
typedef struct {
/** The time interval for TOTP rotation. */
size_t interval; /* 30 == Google OTP */
/** The number of digits in the OTP. */
size_t digits; /* 6 == Google OTP */
/** The time offset (in `interval` units) from the current time. */
int64_t offset; /* 0 == Google OTP */
/** Set to true if the secret / key is in Hex instead of Byte32 encoding. */
uint8_t is_hex;
/** Set to true if the secret / key is raw bit data (no encoding). */
uint8_t is_raw;
} fio_otp_settings_s
```
_Symbol type:_ `type`
### Functions
#### `fio_otp_generate_key`
```c
inline fio_u128 fio_otp_generate_key(void)
```
Generates a random 128 bit key for TOTP processing.
_Symbol type:_ `function`
#### `fio_otp_print_key`
```c
inline size_t fio_otp_print_key(char *dest, uint8_t *key, size_t len)
```
Prints out an OTP secret (big endian number) as a Byte32 encoded String.
_Symbol type:_ `function`
#### `fio_otp`
```c
uint32_t fio_otp(fio_buf_info_s secret, fio_otp_settings_s settings)
```
Returns a TOTP based on `secret` and the otp settings.
_Symbol type:_ `function`
#### `fio_otp`
```c
#define fio_otp(secret, ...) fio_otp(secret, (fio_otp_settings_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_otp_at`
```c
uint32_t fio_otp_at(fio_buf_info_s secret, uint64_t unix_time, fio_otp_settings_s settings)
```
Returns a TOTP for a specific unix timestamp (for testing/verification).
This is useful for verifying OTPs at specific times or for RFC test vectors.
_Symbol type:_ `function`
#### `fio_otp_at`
```c
#define fio_otp_at(secret, unix_time, ...) \
fio_otp_at(secret, unix_time, (fio_otp_settings_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/159 secret.h`
5 public symbols.
### Functions
#### `fio_secret_is_random`
```c
bool fio_secret_is_random(void)
```
Returns true if the secret was randomly generated.
_Symbol type:_ `function`
#### `fio_secret`
```c
fio_u512 fio_secret(void)
```
Gets the SHA512 of a (possibly shared) secret.
_Symbol type:_ `function`
#### `fio_secret_set`
```c
void fio_secret_set(char *str, size_t len, bool is_random)
```
Sets a (possibly shared) secret and stores its SHA512 hash.
_Symbol type:_ `function`
#### `fio_secret_set_at`
```c
void fio_secret_set_at(fio_u512 *secret, char *str, size_t len)
```
Sets a (possibly shared) secret and stores its SHA512 hash.
_Symbol type:_ `function`
#### `fio_secret_at`
```c
fio_u512 fio_secret_at(fio_u512 *secret)
```
Gets the SHA512 of a (possibly shared) secret.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/162 brotli.h`
4 public symbols.
### Functions
#### `fio_brotli_decompress`
```c
size_t fio_brotli_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses Brotli-compressed data (RFC 7932).
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: performs a header scan summing MLEN
values and returns the required size (0 if data is corrupt).
This allows callers to query the decompressed size without allocating.
_Symbol type:_ `function`
#### `fio_brotli_decompress_bound`
```c
inline size_t fio_brotli_decompress_bound(size_t in_len)
```
Conservative upper bound on decompressed size for a Brotli stream.
_Symbol type:_ `function`
#### `fio_brotli_compress`
```c
size_t fio_brotli_compress(void *out, size_t out_len, const void *in, size_t in_len, int quality)
```
Compresses data using Brotli (RFC 7932).
Returns compressed length on success, 0 on error.
quality: 1-6 (1=fast greedy, 3-4=lazy matching, 5-6=hash chain + context
modeling + static dictionary). Caller provides output buffer sized via
fio_brotli_compress_bound().
_Symbol type:_ `function`
#### `fio_brotli_compress_bound`
```c
inline size_t fio_brotli_compress_bound(size_t in_len)
```
Upper bound on Brotli compressed output size.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/162 deflate.h`
13 public symbols.
### Types
#### `fio_deflate_s`
```c
struct fio_deflate_s
```
Opaque streaming deflate/inflate state.
_Symbol type:_ `type`
### Functions
#### `fio_deflate_decompress`
```c
size_t fio_deflate_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses raw DEFLATE data (no zlib/gzip headers).
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: performs a full decode pass counting
output bytes and returns the required size (0 if data is corrupt).
This allows callers to query the decompressed size without allocating.
_Symbol type:_ `function`
#### `fio_deflate_decompress_bound`
```c
inline size_t fio_deflate_decompress_bound(size_t in_len)
```
Conservative upper bound on decompressed size.
_Symbol type:_ `function`
#### `fio_deflate_compress`
```c
size_t fio_deflate_compress(void *out, size_t out_len, const void *in, size_t in_len, int level)
```
Compresses data using raw DEFLATE (no zlib/gzip headers).
Returns compressed length on success, 0 on error.
level: 0=store, 1-3=fast, 4-6=normal, 7-9=best compression.
_Symbol type:_ `function`
#### `fio_deflate_compress_bound`
```c
inline size_t fio_deflate_compress_bound(size_t in_len)
```
Upper bound on compressed output size.
_Symbol type:_ `function`
#### `fio_gzip_compress`
```c
size_t fio_gzip_compress(void *out, size_t out_len, const void *in, size_t in_len, int level)
```
Compresses data with gzip wrapper (for HTTP Content-Encoding).
Returns total output length on success, 0 on error.
_Symbol type:_ `function`
#### `fio_gzip_decompress`
```c
size_t fio_gzip_decompress(void *out, size_t out_len, const void *in, size_t in_len)
```
Decompresses gzip data.
Returns:
- On success: decompressed byte count (<= out_len).
- On buffer too small: the REQUIRED buffer size (> out_len).
- On corrupt/invalid data: 0.
- When out==NULL or out_len==0: returns required size (0 if corrupt).
_Symbol type:_ `function`
#### `fio_deflate_new`
```c
fio_deflate_s *fio_deflate_new(int level, int is_compress)
```
Creates a new streaming deflate/inflate state.
`level`: compression level 1-9 (ignored for decompression).
`is_compress`: non-zero for compression, 0 for decompression.
Returns NULL on allocation failure.
_Symbol type:_ `function`
#### `fio_deflate_new_takeover`
```c
fio_deflate_s *fio_deflate_new_takeover(int level, int is_compress)
```
Creates a streaming state with context takeover (cross-message history
over the last 32KB). Allocates the window (+ compressor hash) inside the
context's own block — the documented per-context cost (~160KB compressor,
~32KB decompressor). `fio_deflate_new` (no-takeover) is the cheap default
and the only mode the WebSocket layer negotiates.
_Symbol type:_ `function`
#### `fio_deflate_free`
```c
void fio_deflate_free(fio_deflate_s *s)
```
Frees a streaming deflate/inflate state.
_Symbol type:_ `function`
#### `fio_deflate_destroy`
```c
void fio_deflate_destroy(fio_deflate_s *s)
```
Resets a deflate streaming context (keeps allocated memory, clears state).
Frees the input buffer when it grew past 64KB, keeping persistent
per-connection state bounded (no-takeover design).
_Symbol type:_ `function`
#### `fio_deflate_window_bits_set`
```c
void fio_deflate_window_bits_set(fio_deflate_s *s, int bits)
```
Clamps compressor match distances to 2^bits (8..15, default 15).
Used to honor `server_max_window_bits` from RFC 7692 negotiation.
_Symbol type:_ `function`
#### `fio_deflate_push`
```c
size_t fio_deflate_push(fio_deflate_s *s, void *out, size_t out_len, const void *in, size_t in_len, int flush)
```
Streaming compress/decompress (no-takeover: each flushed message is an
independent deflate stream — there is no cross-message history mode).
Processes `in_len` bytes from `in`, writing output to `out` (max `out_len`).
`flush`: 0=normal, 1=sync_flush (for WebSocket frame boundaries).
Decompression returns:
- Decompressed byte count on success (<= out_len).
- Required buffer size when buffer too small (> out_len). Internal buffer is
preserved for retry with a larger output buffer.
- 0 on corrupt/invalid data or allocation failure.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/190 tls13.h`
60 public symbols.
### Macros
#### `FIO_TLS13_SHA256_HASH_LEN`
```c
#define FIO_TLS13_SHA256_HASH_LEN 32
```
SHA-256 hash length (32 bytes).
_Symbol type:_ `macro`
#### `FIO_TLS13_SHA384_HASH_LEN`
```c
#define FIO_TLS13_SHA384_HASH_LEN 48
```
SHA-384 hash length (48 bytes).
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_HASH_LEN`
```c
#define FIO_TLS13_MAX_HASH_LEN 48
```
Maximum hash length supported.
_Symbol type:_ `macro`
#### `FIO_TLS13_AES128_KEY_LEN`
```c
#define FIO_TLS13_AES128_KEY_LEN 16
```
AES-128-GCM key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_AES256_KEY_LEN`
```c
#define FIO_TLS13_AES256_KEY_LEN 32
```
AES-256-GCM key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_CHACHA_KEY_LEN`
```c
#define FIO_TLS13_CHACHA_KEY_LEN 32
```
ChaCha20-Poly1305 key length.
_Symbol type:_ `macro`
#### `FIO_TLS13_IV_LEN`
```c
#define FIO_TLS13_IV_LEN 12
```
IV length for all AEAD ciphers.
_Symbol type:_ `macro`
#### `FIO_TLS13_RECORD_HEADER_LEN`
```c
#define FIO_TLS13_RECORD_HEADER_LEN 5
```
TLS record header length (5 bytes)
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_PLAINTEXT_LEN`
```c
#define FIO_TLS13_MAX_PLAINTEXT_LEN 16384
```
Maximum plaintext fragment length (2^14 = 16384 bytes)
_Symbol type:_ `macro`
#### `FIO_TLS13_MAX_CIPHERTEXT_LEN`
```c
#define FIO_TLS13_MAX_CIPHERTEXT_LEN (16384 + 256)
```
Maximum ciphertext length (plaintext + padding + tag)
_Symbol type:_ `macro`
#### `FIO_TLS13_TAG_LEN`
```c
#define FIO_TLS13_TAG_LEN 16
```
AEAD authentication tag length (16 bytes for all TLS 1.3 ciphers)
_Symbol type:_ `macro`
#### `FIO_TLS13_LEGACY_VERSION_MAJOR`
```c
#define FIO_TLS13_LEGACY_VERSION_MAJOR 0x03
```
Legacy TLS version bytes (0x0303 = TLS 1.2)
_Symbol type:_ `macro`
#### `FIO_TLS13_LEGACY_VERSION_MINOR`
```c
#define FIO_TLS13_LEGACY_VERSION_MINOR 0x03
```
_Symbol type:_ `macro`
#### `FIO_TLS13_VERSION_TLS12`
```c
#define FIO_TLS13_VERSION_TLS12 0x0303
```
TLS 1.3 Protocol Version Constants
_Symbol type:_ `macro`
#### `FIO_TLS13_VERSION_TLS13`
```c
#define FIO_TLS13_VERSION_TLS13 0x0304
```
_Symbol type:_ `macro`
#### `FIO_TLS13_HRR_RANDOM`
```c
#define FIO_TLS13_HRR_RANDOM \
"\xCF\x21\xAD\x74\xE5\x9A\x61\x11\xBE\x1D\x8C\x02\x1E\x65\xB8\x91" \
"\xC2\xA2\x11\x16\x7A\xBB\x8C\x5E\x07\x9E\x09\xE2\xC8\xA8\x33\x9C"
```
HelloRetryRequest magic random value (RFC 8446 Section 4.1.3)
_Symbol type:_ `macro`
### Types
#### `fio_tls13_content_type_e`
```c
typedef enum {
FIO_TLS13_CONTENT_INVALID = 0,
FIO_TLS13_CONTENT_CHANGE_CIPHER_SPEC = 20, /* Legacy, ignored in TLS 1.3 */
FIO_TLS13_CONTENT_ALERT = 21,
FIO_TLS13_CONTENT_HANDSHAKE = 22,
FIO_TLS13_CONTENT_APPLICATION_DATA = 23,
} fio_tls13_content_type_e
```
TLS 1.3 content types
_Symbol type:_ `type`
#### `fio_tls13_handshake_type_e`
```c
typedef enum {
FIO_TLS13_HS_CLIENT_HELLO = 1,
FIO_TLS13_HS_SERVER_HELLO = 2,
FIO_TLS13_HS_NEW_SESSION_TICKET = 4,
FIO_TLS13_HS_END_OF_EARLY_DATA = 5,
FIO_TLS13_HS_ENCRYPTED_EXTENSIONS = 8,
FIO_TLS13_HS_CERTIFICATE = 11,
FIO_TLS13_HS_CERTIFICATE_REQUEST = 13,
FIO_TLS13_HS_CERTIFICATE_VERIFY = 15,
FIO_TLS13_HS_FINISHED = 20,
FIO_TLS13_HS_KEY_UPDATE = 24,
FIO_TLS13_HS_MESSAGE_HASH = 254,
} fio_tls13_handshake_type_e
```
TLS 1.3 Handshake Message Types
_Symbol type:_ `type`
#### `fio_tls13_key_update_request_e`
```c
typedef enum {
FIO_TLS13_KEY_UPDATE_NOT_REQUESTED = 0,
FIO_TLS13_KEY_UPDATE_REQUESTED = 1,
} fio_tls13_key_update_request_e
```
TLS 1.3 KeyUpdate Request Types (RFC 8446 Section 4.6.3)
_Symbol type:_ `type`
#### `fio_tls13_extension_type_e`
```c
typedef enum {
FIO_TLS13_EXT_SERVER_NAME = 0, /* SNI */
FIO_TLS13_EXT_SUPPORTED_GROUPS = 10, /* Key exchange groups */
FIO_TLS13_EXT_SIGNATURE_ALGORITHMS = 13, /* Signature schemes */
FIO_TLS13_EXT_ALPN = 16, /* Application-Layer Protocol Negotiation */
FIO_TLS13_EXT_SUPPORTED_VERSIONS = 43, /* TLS version negotiation */
FIO_TLS13_EXT_COOKIE = 44, /* Cookie for HRR (RFC 8446 4.2.2) */
FIO_TLS13_EXT_CERTIFICATE_AUTHORITIES =
47, /* Acceptable CAs (RFC 8446 4.2.4) */
FIO_TLS13_EXT_SIGNATURE_ALGORITHMS_CERT = 50, /* Cert chain sig algs */
FIO_TLS13_EXT_KEY_SHARE = 51, /* ECDHE key shares */
} fio_tls13_extension_type_e
```
TLS 1.3 Extension Types (RFC 8446 Section 4.2)
_Symbol type:_ `type`
#### `fio_tls13_cipher_suite_e`
```c
typedef enum {
FIO_TLS13_CIPHER_SUITE_AES_128_GCM_SHA256 = 0x1301,
FIO_TLS13_CIPHER_SUITE_AES_256_GCM_SHA384 = 0x1302,
FIO_TLS13_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 = 0x1303,
} fio_tls13_cipher_suite_e
```
TLS 1.3 Cipher Suites (RFC 8446 Section B.4)
_Symbol type:_ `type`
#### `fio_tls13_named_group_e`
```c
typedef enum {
FIO_TLS13_GROUP_SECP256R1 = 23, /* P-256 */
FIO_TLS13_GROUP_SECP384R1 = 24, /* P-384 */
FIO_TLS13_GROUP_X25519 = 29, /* Curve25519 */
FIO_TLS13_GROUP_X25519MLKEM768 =
0x11ec, /* X25519 + ML-KEM-768 hybrid (PQC) */
} fio_tls13_named_group_e
```
TLS 1.3 Named Groups (RFC 8446 Section 4.2.7, draft-ietf-tls-hybrid-design)
_Symbol type:_ `type`
#### `fio_tls13_signature_algo_e`
```c
typedef enum {
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA256 = 0x0401,
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA384 = 0x0501,
FIO_TLS13_SIGNATURE_RSA_PKCS1_SHA512 = 0x0601,
FIO_TLS13_SIGNATURE_ECDSA_SECP256R1_SHA256 = 0x0403,
FIO_TLS13_SIGNATURE_ECDSA_SECP384R1_SHA384 = 0x0503,
FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA256 = 0x0804,
FIO_TLS13_SIGNATURE_RSA_PSS_RSAE_SHA384 = 0x0805,
FIO_TLS13_SIGNATURE_ED25519 = 0x0807,
} fio_tls13_signature_algo_e
```
TLS 1.3 Signature Algorithms (RFC 8446 Section 4.2.3)
_Symbol type:_ `type`
#### `fio_tls13_server_hello_s`
```c
typedef struct {
uint8_t random[32]; /* Server random */
uint16_t cipher_suite; /* Selected cipher suite */
uint8_t
key_share[1120]; /* Server's key share (max size for X25519MLKEM768) */
uint16_t key_share_len; /* Length of key share */
uint16_t key_share_group; /* Selected group */
int is_hello_retry_request; /* 1 if HRR */
} fio_tls13_server_hello_s
```
Parsed ServerHello message
_Symbol type:_ `type`
#### `fio_tls13_encrypted_extensions_s`
```c
typedef struct {
int has_server_name; /* Server acknowledged SNI */
char alpn_selected[256]; /* Selected ALPN protocol (null-terminated) */
size_t alpn_selected_len; /* Length of selected ALPN protocol */
} fio_tls13_encrypted_extensions_s
```
Parsed EncryptedExtensions message
_Symbol type:_ `type`
#### `fio_tls13_certificate_s`
```c
typedef struct {
fio_ubuf_info_s cert; /* First certificate (view into message data) */
} fio_tls13_certificate_s
```
Parsed Certificate message (minimal - first cert only)
_Symbol type:_ `type`
#### `fio_tls13_certificate_verify_s`
```c
typedef struct {
fio_ubuf_info_s signature; /* Signature (view into message data) */
uint16_t signature_algo;
} fio_tls13_certificate_verify_s
```
Parsed CertificateVerify message
_Symbol type:_ `type`
#### `fio_tls13_certificate_request_s`
```c
typedef struct {
fio_ubuf_info_s authorities; /* Raw CA DNs data (view into message) */
size_t context_len; /* Context length (0-255) */
size_t algo_count; /* Number of signature algorithms */
size_t cert_algos_count; /* Number of cert sig algos */
uint16_t algos[16]; /* Required signature algorithms */
uint16_t cert_algos[16]; /* Cert chain sig algos (optional) */
uint8_t context[255]; /* Opaque context */
} fio_tls13_certificate_request_s
```
Parsed CertificateRequest message (RFC 8446 Section 4.3.2)
_Symbol type:_ `type`
#### `fio_tls13_cipher_type_e`
```c
typedef enum {
FIO_TLS13_CIPHER_AES_128_GCM = 0, /* TLS_AES_128_GCM_SHA256 */
FIO_TLS13_CIPHER_AES_256_GCM = 1, /* TLS_AES_256_GCM_SHA384 */
FIO_TLS13_CIPHER_CHACHA20_POLY1305 = 2, /* TLS_CHACHA20_POLY1305_SHA256 */
} fio_tls13_cipher_type_e
```
Supported AEAD cipher types for TLS 1.3
_Symbol type:_ `type`
#### `fio_tls13_plaintext_header_s`
```c
typedef struct {
uint8_t content_type; /* ContentType */
uint8_t legacy_version[2]; /* 0x03, 0x03 (TLS 1.2) */
uint16_t length; /* Fragment length (big-endian) */
/* Fragment follows (up to 2^14 bytes) */
} fio_tls13_plaintext_header_s
```
TLSPlaintext header structure (RFC 8446 Section 5.1)
_Symbol type:_ `type`
#### `fio_tls13_ciphertext_header_s`
```c
typedef struct {
uint8_t opaque_type; /* Always 23 (application_data) */
uint8_t legacy_version[2]; /* 0x03, 0x03 */
uint16_t length; /* Encrypted length + tag (big-endian) */
/* Encrypted content follows */
} fio_tls13_ciphertext_header_s
```
TLSCiphertext header structure (RFC 8446 Section 5.2)
_Symbol type:_ `type`
#### `fio_tls13_record_keys_s`
```c
typedef struct {
uint8_t key[32]; /* Write key (16 or 32 bytes depending on cipher) */
uint8_t iv[12]; /* Write IV (always 12 bytes) */
uint64_t sequence_number; /* Per-record sequence number (starts at 0) */
uint8_t key_len; /* 16 for AES-128, 32 for AES-256/ChaCha20 */
uint8_t cipher_type; /* fio_tls13_cipher_type_e */
} fio_tls13_record_keys_s
```
Record encryption context (per-direction keys)
_Symbol type:_ `type`
### Functions
#### `fio_tls13_hkdf_expand_label`
```c
void fio_tls13_hkdf_expand_label(void *restrict out, size_t out_len, const void *restrict secret, size_t secret_len, const char *label, size_t label_len, const void *restrict context, size_t context_len, int use_sha384)
```
TLS 1.3 HKDF-Expand-Label function.
Derives keying material using the TLS 1.3 specific label format.
**Parameters:**
- `out` - Output buffer for derived key material
- `out_len` - Desired output length (max 255)
- `secret` - The secret to expand (PRK from HKDF-Extract)
- `secret_len` - Secret length (32 for SHA-256, 48 for SHA-384)
- `label` - The label string (without "tls13 " prefix)
- `label_len` - Label length (max 249 to fit in 255 with prefix)
- `context` - Optional context (transcript hash or empty)
- `context_len` - Context length (max 255)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_secret`
```c
void fio_tls13_derive_secret(void *restrict out, const void *restrict secret, size_t secret_len, const char *label, size_t label_len, const void *restrict transcript_hash, size_t hash_len, int use_sha384)
```
TLS 1.3 Derive-Secret function.
Derives a secret from a base secret and transcript hash.
**Parameters:**
- `out` - Output buffer (32 bytes for SHA-256, 48 for SHA-384)
- `secret` - The base secret
- `secret_len` - Secret length
- `label` - The label string (e.g., "c hs traffic")
- `label_len` - Label length
- `transcript_hash` - Hash of handshake messages (or empty hash for "")
- `hash_len` - Hash length (32 or 48)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_early_secret`
```c
void fio_tls13_derive_early_secret(void *restrict early_secret, const void *restrict psk, size_t psk_len, int use_sha384)
```
Derive the Early Secret from PSK.
Early Secret = HKDF-Extract(salt=0, IKM=PSK)
**Parameters:**
- `early_secret` - Output buffer (32 or 48 bytes)
- `psk` - Pre-shared key (or NULL/zeros for no PSK)
- `psk_len` - PSK length (0 if no PSK)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_handshake_secret`
```c
void fio_tls13_derive_handshake_secret(void *restrict handshake_secret, const void *restrict early_secret, const void *restrict ecdhe_secret, size_t ecdhe_len, int use_sha384)
```
Derive the Handshake Secret from ECDHE shared secret.
Handshake Secret = HKDF-Extract(
salt=Derive-Secret(Early Secret, "derived", ""),
IKM=ECDHE shared secret
)
**Parameters:**
- `handshake_secret` - Output buffer (32 or 48 bytes)
- `early_secret` - The early secret (from fio_tls13_derive_early_secret)
- `ecdhe_secret` - The ECDHE shared secret (e.g., from X25519)
- `ecdhe_len` - ECDHE secret length (32 for X25519)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_master_secret`
```c
void fio_tls13_derive_master_secret(void *restrict master_secret, const void *restrict handshake_secret, int use_sha384)
```
Derive the Master Secret.
Master Secret = HKDF-Extract(
salt=Derive-Secret(Handshake Secret, "derived", ""),
IKM=0
)
**Parameters:**
- `master_secret` - Output buffer (32 or 48 bytes)
- `handshake_secret` - The handshake secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_traffic_keys`
```c
void fio_tls13_derive_traffic_keys(void *restrict key, size_t key_len, void *restrict iv, const void *restrict traffic_secret, int use_sha384)
```
Derive traffic keys and IV from a traffic secret.
**Parameters:**
- `key` - Output buffer for write key
- `key_len` - Key length (16 for AES-128, 32 for AES-256/ChaCha20)
- `iv` - Output buffer for write IV (12 bytes)
- `traffic_secret` - The traffic secret (client/server handshake/app)
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_derive_finished_key`
```c
void fio_tls13_derive_finished_key(void *restrict finished_key, const void *restrict traffic_secret, int use_sha384)
```
Derive the Finished key from a traffic secret.
finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
**Parameters:**
- `finished_key` - Output buffer (32 or 48 bytes)
- `traffic_secret` - The handshake traffic secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_compute_finished`
```c
void fio_tls13_compute_finished(void *restrict verify_data, const void *restrict finished_key, const void *restrict transcript_hash, int use_sha384)
```
Compute the Finished verify_data.
verify_data = HMAC(finished_key, Transcript-Hash(Handshake Context))
**Parameters:**
- `verify_data` - Output buffer (32 or 48 bytes)
- `finished_key` - The finished key (from fio_tls13_derive_finished_key)
- `transcript_hash` - Hash of handshake messages up to this point
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_update_traffic_secret`
```c
void fio_tls13_update_traffic_secret(void *restrict new_secret, const void *restrict current_secret, int use_sha384)
```
Update application traffic secret for key update.
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N, "traffic upd", "",
Hash.length)
**Parameters:**
- `new_secret` - Output buffer (32 or 48 bytes)
- `current_secret` - Current application traffic secret
- `use_sha384` - If non-zero, use SHA-384; otherwise SHA-256
_Symbol type:_ `function`
#### `fio_tls13_build_nonce`
```c
inline void fio_tls13_build_nonce(uint8_t nonce[12], const uint8_t iv[12], uint64_t seq)
```
Build per-record nonce by XORing sequence number with IV.
Per RFC 8446 Section 5.3:
- Pad 64-bit sequence number to 12 bytes (big-endian, left-padded with zeros)
- XOR with the static IV derived from traffic secret
**Parameters:**
- `nonce` - Output buffer (must be 12 bytes)
- `iv` - Static IV from key derivation (12 bytes)
- `seq` - 64-bit sequence number
_Symbol type:_ `function`
#### `fio_tls13_record_parse_header`
```c
const uint8_t *fio_tls13_record_parse_header( const uint8_t *data, size_t data_len, fio_tls13_content_type_e *content_type, size_t *payload_len)
```
Parse a TLS record header.
**Parameters:**
- `data` - Input buffer containing record data
- `data_len` - Length of input buffer
- `content_type` - Output: content type from header
- `payload_len` - Output: payload length from header
**Returns:**
- Pointer to payload data, or NULL if incomplete/invalid
_Symbol type:_ `function`
#### `fio_tls13_record_encrypt`
```c
int fio_tls13_record_encrypt(uint8_t *out, size_t out_capacity, const uint8_t *plaintext, size_t plaintext_len, fio_tls13_content_type_e content_type, fio_tls13_record_keys_s *keys)
```
Encrypt a TLS 1.3 record.
Per RFC 8446 Section 5.2:
- Output format: 5-byte header + encrypted(plaintext + content_type) + tag
- AAD is the 5-byte record header
- Sequence number is incremented after encryption
**Parameters:**
- `out` - Output buffer for encrypted record
- `out_capacity` - Capacity of output buffer
- `plaintext` - Plaintext data to encrypt
- `plaintext_len` - Length of plaintext
- `content_type` - Content type (appended to plaintext before encryption)
- `keys` - Encryption keys (sequence number will be incremented)
**Returns:**
- Total output length (header + ciphertext + tag), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_record_decrypt`
```c
int fio_tls13_record_decrypt(uint8_t *out, size_t out_capacity, fio_tls13_content_type_e *content_type, const uint8_t *ciphertext, size_t ciphertext_len, fio_tls13_record_keys_s *keys)
```
Decrypt a TLS 1.3 record.
Per RFC 8446 Section 5.2:
- Input includes 5-byte header
- Decrypts and verifies AEAD tag
- Scans backwards to find real content type (removes padding)
- Sequence number is incremented after successful decryption
Note: decryption is performed in-place in the ciphertext buffer, so the
ciphertext buffer will be modified. The output buffer only needs to hold
the plaintext (not the internal content type byte).
**Parameters:**
- `out` - Output buffer for decrypted plaintext
- `out_capacity` - Capacity of output buffer (>= plaintext length)
- `content_type` - Output: actual content type from inner plaintext
- `ciphertext` - Input ciphertext (includes 5-byte header, modified)
- `ciphertext_len` - Total length including header
- `keys` - Decryption keys (sequence number will be incremented)
**Returns:**
- Plaintext length (excluding padding and content type), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_record_keys_init`
```c
void fio_tls13_record_keys_init(fio_tls13_record_keys_s *keys, const uint8_t *key, uint8_t key_len, const uint8_t iv[12], fio_tls13_cipher_type_e cipher_type)
```
Initialize record keys structure.
**Parameters:**
- `keys` - Keys structure to initialize
- `key` - Key material (16 or 32 bytes)
- `key_len` - Key length (16 for AES-128, 32 for AES-256/ChaCha20)
- `iv` - IV material (12 bytes)
- `cipher_type` - Cipher type (fio_tls13_cipher_type_e)
_Symbol type:_ `function`
#### `fio_tls13_record_keys_clear`
```c
inline void fio_tls13_record_keys_clear(fio_tls13_record_keys_s *keys)
```
Clear sensitive key material from memory.
**Parameters:**
- `keys` - Keys structure to clear
_Symbol type:_ `function`
#### `fio_tls13_parse_handshake_header`
```c
const uint8_t *fio_tls13_parse_handshake_header( const uint8_t *data, size_t data_len, fio_tls13_handshake_type_e *msg_type, size_t *body_len)
```
Parse handshake header, return message type and body pointer.
Returns pointer to message body, or NULL on error.
Sets msg_type and body_len on success.
_Symbol type:_ `function`
#### `fio_tls13_write_handshake_header`
```c
void fio_tls13_write_handshake_header(uint8_t *out, fio_tls13_handshake_type_e msg_type, size_t body_len)
```
Write handshake header (4 bytes).
Format: HandshakeType (1 byte) + uint24 length (3 bytes)
_Symbol type:_ `function`
#### `fio_tls13_build_client_hello`
```c
int fio_tls13_build_client_hello(uint8_t *out, size_t out_capacity, const uint8_t random[32], const char *server_name, const uint8_t *x25519_pubkey, const uint16_t *cipher_suites, size_t cipher_suite_count)
```
Build a ClientHello message.
Returns: message length on success, -1 on error.
Parameters:
- out: output buffer
- out_capacity: size of output buffer
- random: 32-byte client random
- server_name: SNI hostname (NULL if not used)
- x25519_pubkey: 32-byte X25519 public key
- cipher_suites: array of cipher suites to offer
- cipher_suite_count: number of cipher suites
_Symbol type:_ `function`
#### `fio_tls13_parse_server_hello`
```c
int fio_tls13_parse_server_hello(fio_tls13_server_hello_s *out, const uint8_t *data, size_t data_len)
```
Parse ServerHello message.
Returns: 0 on success, -1 on error.
Note: data should point to the handshake body (after the 4-byte header).
_Symbol type:_ `function`
#### `fio_tls13_parse_encrypted_extensions`
```c
int fio_tls13_parse_encrypted_extensions( fio_tls13_encrypted_extensions_s *out, const uint8_t *data, size_t data_len)
```
Parse EncryptedExtensions message.
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_certificate`
```c
int fio_tls13_parse_certificate(fio_tls13_certificate_s *out, const uint8_t *data, size_t data_len)
```
Parse Certificate message (extracts first certificate only).
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_certificate_verify`
```c
int fio_tls13_parse_certificate_verify( fio_tls13_certificate_verify_s *out, const uint8_t *data, size_t data_len)
```
Parse CertificateVerify message.
Returns: 0 on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_build_finished`
```c
int fio_tls13_build_finished(uint8_t *out, size_t out_capacity, const uint8_t *verify_data, size_t verify_data_len)
```
Build Finished message.
verify_data = HMAC(finished_key, Transcript-Hash(Handshake Context))
Returns: message length on success, -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_parse_finished`
```c
int fio_tls13_parse_finished(const uint8_t *data, size_t data_len, const uint8_t *expected_verify_data, size_t verify_data_len)
```
Parse and verify Finished message.
Returns: 0 on success (MAC matches), -1 on error.
_Symbol type:_ `function`
#### `fio_tls13_build_alert`
```c
inline int fio_tls13_build_alert(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc)
```
Build an alert message (unencrypted, for use before handshake keys).
Alert format: [level:1][description:1]
**Parameters:**
- `out` - Output buffer for alert message (2 bytes minimum)
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
**Returns:**
- Alert message length (2), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_send_alert`
```c
int fio_tls13_send_alert(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc, fio_tls13_record_keys_s *keys)
```
Build an encrypted alert record.
Per RFC 8446 Section 6, alerts are encrypted after handshake keys are
established. In TLS 1.3, all alerts except close_notify are effectively
fatal and the connection must be closed after sending.
**Parameters:**
- `out` - Output buffer for encrypted alert record
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
- `keys` - Encryption keys (sequence number will be incremented)
**Returns:**
- Encrypted record length, or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_send_alert_plaintext`
```c
int fio_tls13_send_alert_plaintext(uint8_t *out, size_t out_capacity, uint8_t alert_level, uint8_t alert_desc)
```
Build an unencrypted alert record (for use before encryption is enabled).
**Parameters:**
- `out` - Output buffer for alert record
- `out_capacity` - Capacity of output buffer
- `alert_level` - Alert level (1=warning, 2=fatal)
- `alert_desc` - Alert description code
**Returns:**
- Record length (7 bytes: 5 header + 2 alert), or -1 on error
_Symbol type:_ `function`
#### `fio_tls13_alert_name`
```c
inline const char *fio_tls13_alert_name(uint8_t alert_desc)
```
Get human-readable name for an alert description.
**Parameters:**
- `alert_desc` - Alert description code
**Returns:**
- Static string with alert name
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/201 string.h`
47 public symbols.
### Macros
#### `FIO_STR_OPTIMIZE_EMBEDDED`
```c
#define FIO_STR_OPTIMIZE_EMBEDDED 0
```
For each unit (0 by default), adds `sizeof(char *)` bytes to the type size,
increasing the amount of strings that could be embedded within the type
without additional memory allocation.
For example, when using a reference counter wrapper on a 64bit system, it
would make sense to set this value to 1 - allowing the type size to fully
utilize a 16 byte memory allocation alignment.
_Symbol type:_ `macro`
#### `FIO_STR_OPTIMIZE4IMMUTABILITY`
```c
#define FIO_STR_OPTIMIZE4IMMUTABILITY 0
```
Minimizes the struct size, storing only string length and pointer.
By avoiding extra (mutable related) data, such as the allocated memory's
capacity, strings require less memory. However, this does introduce a
performance penalty when editing the string data.
_Symbol type:_ `macro`
#### `FIO_STR_INIT`
```c
#define FIO_STR_INIT \
{ .special = 0 }
```
This value should be used for initialization. For example:
// on the stack
fio_str_s str = FIO_STR_INIT;
// or on the heap
fio_str_s *str = malloc(sizeof(*str));
*str = FIO_STR_INIT;
Remember to cleanup:
// on the stack
fio_str_destroy(&str);
// or on the heap
fio_str_free(str);
free(str);
_Symbol type:_ `macro`
#### `FIO_STR_INIT_EXISTING`
```c
#define FIO_STR_INIT_EXISTING(buffer, length, capacity) \
{ .capa = (capacity), .len = (length), .buf = (buffer) }
```
This macro allows the container to be initialized with existing data, as long
as it's memory was allocated with the same allocator (`malloc` /
`fio_malloc`).
The `capacity` value should exclude the NUL character (if exists).
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_INIT_STATIC`
```c
#define FIO_STR_INIT_STATIC(buffer) \
{ \
.special = 4, .capa = FIO_STRLEN((buffer)), .len = FIO_STRLEN((buffer)), \
.buf = (char *)(buffer) \
}
```
This macro allows the container to be initialized with existing static data,
that shouldn't be freed.
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_INIT_STATIC2`
```c
#define FIO_STR_INIT_STATIC2(buffer, length) \
{ .special = 4, .capa = (length), .len = (length), .buf = (char *)(buffer) }
```
This macro allows the container to be initialized with existing static data,
that shouldn't be freed.
NOTE: This macro isn't valid for FIO_STR_SMALL (or strings with the
FIO_STR_OPTIMIZE4IMMUTABILITY optimization)
_Symbol type:_ `macro`
#### `FIO_STR_WRITE2`
```c
#define FIO_STR_WRITE2(str_name, dest, ...) \
FIO_NAME(str_name, __write2)(dest, (fio_string_write_s[]){__VA_ARGS__, {0}})
```
_Symbol type:_ `macro`
### Types
#### `fiobj_str_s`
```c
typedef struct {
/* String flags:
*
* bit 1: small string.
* bit 2: frozen string.
* bit 3: static (non allocated) string (big strings only).
* bit 3-8: small string length (up to 64 bytes).
*/
uint8_t special;
uint8_t reserved[(sizeof(void *) * (1 + FIO_STR_OPTIMIZE_EMBEDDED)) -
(sizeof(uint8_t))]; /* padding length */
#if !FIO_STR_OPTIMIZE4IMMUTABILITY
size_t capa; /* known capacity for longer Strings */
size_t len; /* String length for longer Strings */
#endif /* FIO_STR_OPTIMIZE4IMMUTABILITY */
char *buf; /* pointer for longer Strings */
} fiobj_str_s
```
The `fio_str_s` type should be considered opaque.
The type's attributes should be accessed ONLY through the accessor
functions: `fio_str2cstr`, `fio_str_len`, `fio_str2ptr`, `fio_str_capa`,
etc'.
Note: when the `small` flag is present, the structure is ignored and used
as raw memory for a small String (no additional allocation). This changes
the String's behavior drastically and requires that the accessor functions
be used.
_Symbol type:_ `type`
### Functions
#### `fiobj_str_init_const`
```c
inline fio_str_info_s fiobj_str_init_const(FIO_STR_PTR s, const char *str, size_t len)
```
Initializes the container with the provided static / constant string.
The string will be copied to the container **only** if it will fit in the
container itself. Otherwise, the supplied pointer will be used as is and it
should remain valid until the string is destroyed.
The final string can be safely be destroyed (using the `destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_init_copy`
```c
inline fio_str_info_s fiobj_str_init_copy(FIO_STR_PTR s, const char *str, size_t len)
```
Initializes the container with a copy of the provided dynamic string.
The string is always copied and the final string must be destroyed (using the
`destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_init_copy2`
```c
inline fio_str_info_s fiobj_str_init_copy2(FIO_STR_PTR dest, FIO_STR_PTR src)
```
Initializes the container with a copy of an existing String object.
The string is always copied and the final string must be destroyed (using the
`destroy` function).
_Symbol type:_ `function`
#### `fiobj_str_destroy`
```c
inline void fiobj_str_destroy(FIO_STR_PTR s)
```
Frees the String's resources and re-initializes the container.
Note: if the container isn't allocated on the stack, it should be freed
separately using the appropriate `free` function.
_Symbol type:_ `function`
#### `fiobj_str_detach`
```c
inline char *fiobj_str_detach(FIO_STR_PTR s)
```
Returns a C string with the existing data, re-initializing the String.
Note: the String data is removed from the container, but the container
isn't freed.
Returns NULL if there's no String data.
NOTE: Returned string is ALWAYS dynamically allocated. Remember to free.
_Symbol type:_ `function`
#### `fiobj_str_dealloc`
```c
inline void fiobj_str_dealloc(void *ptr)
```
Frees the pointer returned by `detach`.
_Symbol type:_ `function`
#### `fiobj_str_info`
```c
inline fio_str_info_s fiobj_str_info(const FIO_STR_PTR s)
```
Returns the String's complete state (capacity, length and pointer).
_Symbol type:_ `function`
#### `fiobj_str_buf`
```c
inline fio_buf_info_s fiobj_str_buf(const FIO_STR_PTR s)
```
Returns the String's partial state (length and pointer).
_Symbol type:_ `function`
#### `fiobj_str_ptr`
```c
inline char *fiobj_str_ptr(FIO_STR_PTR s)
```
Returns a pointer (`char *`) to the String's content.
_Symbol type:_ `function`
#### `fiobj_str_len`
```c
inline size_t fiobj_str_len(FIO_STR_PTR s)
```
Returns the String's length in bytes.
_Symbol type:_ `function`
#### `fiobj_str_capa`
```c
inline size_t fiobj_str_capa(FIO_STR_PTR s)
```
Returns the String's existing capacity (total used & available memory).
_Symbol type:_ `function`
#### `fiobj_str_freeze`
```c
inline void fiobj_str_freeze(FIO_STR_PTR s)
```
Prevents further manipulations to the String's content.
_Symbol type:_ `function`
#### `fiobj_str_is_frozen`
```c
inline uint8_t fiobj_str_is_frozen(FIO_STR_PTR s)
```
Returns true if the string is frozen.
_Symbol type:_ `function`
#### `fiobj_str_is_allocated`
```c
inline int fiobj_str_is_allocated(const FIO_STR_PTR s)
```
Returns 1 if memory was allocated (and the String must be destroyed).
_Symbol type:_ `function`
#### `fiobj_str_is_eq`
```c
inline int fiobj_str_is_eq(const FIO_STR_PTR str1, const FIO_STR_PTR str2)
```
Binary comparison returns `1` if both strings are equal and `0` if not.
_Symbol type:_ `function`
#### `fiobj_str_hash`
```c
inline uint64_t fiobj_str_hash(const FIO_STR_PTR s, uint64_t seed)
```
Returns the string's Risky Hash value.
Note: Hash algorithm might change without notice.
_Symbol type:_ `function`
#### `fiobj_str_resize`
```c
inline fio_str_info_s fiobj_str_resize(FIO_STR_PTR s, size_t size)
```
Sets the new String size without reallocating any memory (limited by
existing capacity).
Returns the updated state of the String.
Note: When shrinking, any existing data beyond the new size may be
corrupted.
_Symbol type:_ `function`
#### `fiobj_str_compact`
```c
inline void fiobj_str_compact(FIO_STR_PTR s)
```
Performs a best attempt at minimizing memory consumption.
Actual effects depend on the underlying memory allocator and it's
implementation. Not all allocators will free any memory.
_Symbol type:_ `function`
#### `fiobj_str_reserve`
```c
fio_str_info_s fiobj_str_reserve(FIO_STR_PTR s, size_t amount)
```
Reserves (at least) `amount` of bytes for the string's data.
`amount` is in addition to existing String length.
Make sure to call `resize` with the updated information once the editing is
done.
Returns the updated state of the String.
_Symbol type:_ `function`
#### `fiobj_str_utf8_valid`
```c
size_t fiobj_str_utf8_valid(FIO_STR_PTR s)
```
Returns 1 if the String is UTF-8 valid and 0 if not.
_Symbol type:_ `function`
#### `fiobj_str_utf8_len`
```c
size_t fiobj_str_utf8_len(FIO_STR_PTR s)
```
Returns the String's length in UTF-8 characters.
_Symbol type:_ `function`
#### `fiobj_str_utf8_select`
```c
int fiobj_str_utf8_select(FIO_STR_PTR s, intptr_t *pos, size_t *len)
```
Takes a UTF-8 character selection information (UTF-8 position and length)
and updates the same variables so they reference the raw byte slice
information.
If the String isn't UTF-8 valid up to the requested selection, than `pos`
will be updated to `-1` otherwise values are always positive.
The returned `len` value may be shorter than the original if there wasn't
enough data left to accommodate the requested length. When a `len` value of
`0` is returned, this means that `pos` marks the end of the String.
Returns -1 on error and 0 on success.
_Symbol type:_ `function`
#### `fiobj_str_write`
```c
inline fio_str_info_s fiobj_str_write(FIO_STR_PTR s, const void *src, size_t src_len)
```
Writes data at the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_concat`
```c
fio_str_info_s fiobj_str_concat(FIO_STR_PTR dest, FIO_STR_PTR const src)
```
Appends the `src` String to the end of the `dest` String.
If `dest` is empty, the resulting Strings will be equal.
_Symbol type:_ `function`
#### `fiobj_str_join`
```c
inline fio_str_info_s fiobj_str_join(FIO_STR_PTR dest, FIO_STR_PTR const src)
```
Alias for fio_str_concat
_Symbol type:_ `function`
#### `fiobj_str_replace`
```c
fio_str_info_s fiobj_str_replace(FIO_STR_PTR s, intptr_t start_pos, size_t old_len, const void *src, size_t src_len)
```
Replaces the data in the String - replacing `old_len` bytes starting at
`start_pos`, with the data at `src` (`src_len` bytes long).
Negative `start_pos` values are calculated backwards, `-1` == end of
String.
When `old_len` is zero, the function will insert the data at `start_pos`.
If `src_len == 0` than `src` will be ignored and the data marked for
replacement will be erased.
_Symbol type:_ `function`
#### `fiobj_str_write_i`
```c
fio_str_info_s fiobj_str_write_i(FIO_STR_PTR s, int64_t num)
```
Writes a number at the end of the String using normal base 10 notation.
_Symbol type:_ `function`
#### `fiobj_str_write_hex`
```c
fio_str_info_s fiobj_str_write_hex(FIO_STR_PTR s, int64_t num)
```
Writes a number at the end of the String using Hex (base 16) notation.
_Symbol type:_ `function`
#### `fiobj_str_write_bin`
```c
fio_str_info_s fiobj_str_write_bin(FIO_STR_PTR s, int64_t num)
```
_Symbol type:_ `function`
#### `fiobj_str_vprintf`
```c
fio_str_info_s fiobj_str_vprintf(FIO_STR_PTR s, const char *format, va_list argv)
```
Writes to the String using a vprintf like interface.
Data is written to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_printf`
```c
fio_str_info_s fiobj_str_printf(FIO_STR_PTR s, const char *format, ...)
```
Writes to the String using a printf like interface.
Data is written to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_write_escape`
```c
fio_str_info_s fiobj_str_write_escape(FIO_STR_PTR s, const void *data, size_t data_len)
```
Writes data at the end of the String, escaping the data using JSON semantics.
The JSON semantic are common to many programming languages, promising a UTF-8
String while making it easy to read and copy the string during debugging.
_Symbol type:_ `function`
#### `fiobj_str_write_unescape`
```c
fio_str_info_s fiobj_str_write_unescape(FIO_STR_PTR s, const void *escaped, size_t len)
```
Writes an escaped data into the string after unescaping the data.
_Symbol type:_ `function`
#### `fiobj_str_write_base64enc`
```c
fio_str_info_s fiobj_str_write_base64enc(FIO_STR_PTR s, const void *data, size_t data_len, uint8_t url_encoded)
```
Writes data at the end of the String, encoding the data as Base64 encoded
data.
_Symbol type:_ `function`
#### `fiobj_str_write_base64dec`
```c
fio_str_info_s fiobj_str_write_base64dec(FIO_STR_PTR s, const void *encoded, size_t encoded_len)
```
Writes decoded base64 data to the end of the String.
_Symbol type:_ `function`
#### `fiobj_str_write_html_escape`
```c
fio_str_info_s fiobj_str_write_html_escape(FIO_STR_PTR s, const void *raw, size_t len)
```
Writes HTML escaped data to a String.
_Symbol type:_ `function`
#### `fiobj_str_write_html_unescape`
```c
fio_str_info_s fiobj_str_write_html_unescape(FIO_STR_PTR s, const void *escaped, size_t len)
```
Writes HTML un-escaped data to a String - incomplete and minimal.
_Symbol type:_ `function`
#### `fiobj_str_readfd`
```c
fio_str_info_s fiobj_str_readfd(FIO_STR_PTR s, int fd, intptr_t start_at, intptr_t limit)
```
Reads data from a file descriptor `fd` at offset `start_at` and pastes it's
contents (or a slice of it) at the end of the String. If `limit == 0`, than
the data will be read until EOF.
The file should be a regular file or the operation might fail (can't be used
for sockets).
The file descriptor will remain open and should be closed manually.
_Symbol type:_ `function`
#### `fiobj_str_readfile`
```c
fio_str_info_s fiobj_str_readfile(FIO_STR_PTR s, const char *filename, intptr_t start_at, intptr_t limit)
```
Opens the file `filename` and pastes it's contents (or a slice ot it) at
the end of the String. If `limit == 0`, than the data will be read until
EOF.
If the file can't be located, opened or read, or if `start_at` is beyond
the EOF position, NULL is returned in the state's `data` field.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/202 array.h`
30 public symbols.
### Macros
#### `FIO_ARRAY_NOT_FOUND`
```c
#define FIO_ARRAY_NOT_FOUND ((uint32_t)-1)
```
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE`
```c
#define FIO_ARRAY_TYPE void *
```
The type for array elements (an array of FIO_ARRAY_TYPE)
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_INVALID`
```c
#define FIO_ARRAY_TYPE_INVALID NULL
```
An invalid value for that type (if any).
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_INVALID_SIMPLE`
```c
#define FIO_ARRAY_TYPE_INVALID_SIMPLE 0
```
Is the FIO_ARRAY_TYPE_INVALID object memory is all zero? (yes = 1)
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_COPY`
```c
#define FIO_ARRAY_TYPE_COPY(dest, src) (dest) = (src)
```
Handles a copy operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_DESTROY`
```c
#define FIO_ARRAY_TYPE_DESTROY(obj)
```
Handles a destroy / free operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_TYPE_CMP`
```c
#define FIO_ARRAY_TYPE_CMP(a, b) (a) == (b)
```
Handles a comparison operation for an array's element.
_Symbol type:_ `macro`
#### `FIO_ARRAY_INIT`
```c
#define FIO_ARRAY_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_ARRAY_EACH`
```c
#define FIO_ARRAY_EACH(array_name, array, pos) \
for (FIO_NAME(array_name, ____type_t) \
*first___ai = NULL, \
*pos = FIO_NAME(array_name, each_next)((array), &first___ai, NULL); \
pos; \
pos = FIO_NAME(array_name, each_next)((array), &first___ai, pos))
```
Iterates through the array using a `for` loop.
Access the object with the pointer `pos`. The `pos` variable can be named
however you please.
Avoid editing the array during a FOR loop, although I hope it's possible, I
wouldn't count on it.
**Note**: this variant supports automatic pointer tagging / untagging.
_Symbol type:_ `macro`
### Types
#### `fiobj_array_s`
```c
struct fiobj_array_s {
/* start common header (with embedded array type) */
/** the offset to the first item. */
uint32_t start;
/** The offset to the first empty location the array. */
uint32_t end;
/* end common header (with embedded array type) */
/** The array's capacity - limited to 32bits, but we use the extra padding. */
uint32_t capa;
uint32_t padding__;
/** a pointer to the array's memory (if not embedded) */
FIO_ARRAY_TYPE *ary;
#if FIO_ARRAY_ENABLE_EMBEDDED > 1
/** Do we wanted larger small-array optimizations? */
FIO_ARRAY_TYPE
extra_memory_for_embedded_arrays[(FIO_ARRAY_ENABLE_EMBEDDED - 1)]
#endif
}
```
an Array type.
_Symbol type:_ `type`
#### `fiobj_array_each_s`
```c
struct fiobj_array_each_s {
/** The array iterated. Once set, cannot be safely changed. */
FIO_ARRAY_PTR const parent;
/** The current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct FIO_NAME(FIO_ARRAY_NAME, each_s) * info);
/** Opaque user data. */
void *udata;
/** The object / value at the current index. */
FIO_ARRAY_TYPE value;
/* memory padding used for FIOBJ */
uint64_t padding;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_array_destroy`
```c
void fiobj_array_destroy(FIO_ARRAY_PTR ary)
```
_Symbol type:_ `function`
#### `fiobj_array_count`
```c
inline uint32_t fiobj_array_count(FIO_ARRAY_PTR ary)
```
Returns the number of elements in the Array.
_Symbol type:_ `function`
#### `fiobj_array_capa`
```c
inline uint32_t fiobj_array_capa(FIO_ARRAY_PTR ary)
```
Returns the current, temporary, array capacity (it's dynamic).
_Symbol type:_ `function`
#### `fiobj_array_is_embedded`
```c
inline int fiobj_array_is_embedded(FIO_ARRAY_PTR ary)
```
Returns 1 if the array is embedded, 0 if it has memory allocated and -1 on an
error.
_Symbol type:_ `function`
#### `fiobj_array2ptr`
```c
inline FIO_ARRAY_TYPE *fiobj_array2ptr(FIO_ARRAY_PTR ary)
```
Returns a pointer to the C array containing the objects.
_Symbol type:_ `function`
#### `fiobj_array_reserve`
```c
uint32_t fiobj_array_reserve(FIO_ARRAY_PTR ary, int64_t capa)
```
Reserves a minimal capacity for additional elements to be added to the array.
If `capa` is negative, new memory will be allocated at the beginning of the
array rather then it's end.
Returns the array's new capacity.
_Symbol type:_ `function`
#### `fiobj_array_concat`
```c
FIO_ARRAY_PTR fiobj_array_concat(FIO_ARRAY_PTR dest, FIO_ARRAY_PTR src)
```
Adds all the items in the `src` Array to the end of the `dest` Array.
The `src` Array remain untouched.
Always returns the destination array (`dest`).
_Symbol type:_ `function`
#### `fiobj_array_set`
```c
FIO_ARRAY_TYPE *fiobj_array_set(FIO_ARRAY_PTR ary, int64_t index, FIO_ARRAY_TYPE data, FIO_ARRAY_TYPE *old)
```
Sets `index` to the value in `data`.
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
If `old` isn't NULL, the existing data will be copied to the location pointed
to by `old` before the copy in the Array is destroyed.
Returns a pointer to the new object, or NULL on error.
_Symbol type:_ `function`
#### `fiobj_array_get`
```c
inline FIO_ARRAY_TYPE fiobj_array_get(FIO_ARRAY_PTR ary, int64_t index)
```
Returns the value located at `index` (no copying is performed).
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
_Symbol type:_ `function`
#### `fiobj_array_find`
```c
uint32_t fiobj_array_find(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data, int64_t start_at)
```
Returns the index of the object or (uint32_t)-1 if the object wasn't found.
If `start_at` is negative (i.e., -1), than seeking will be performed in
reverse, where -1 == last index (-2 == second to last, etc').
_Symbol type:_ `function`
#### `fiobj_array_remove`
```c
int fiobj_array_remove(FIO_ARRAY_PTR ary, int64_t index, FIO_ARRAY_TYPE *old)
```
Removes an object from the array, MOVING all the other objects to prevent
"holes" in the data.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns 0 on success and -1 on error.
This action is O(n) where n in the length of the array.
It could get expensive.
_Symbol type:_ `function`
#### `fiobj_array_remove2`
```c
uint32_t fiobj_array_remove2(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Removes all occurrences of an object from the array (if any), MOVING all the
existing objects to prevent "holes" in the data.
Returns the number of items removed.
This action is O(n) where n in the length of the array.
It could get expensive.
_Symbol type:_ `function`
#### `fiobj_array_compact`
```c
void fiobj_array_compact(FIO_ARRAY_PTR ary)
```
Attempts to lower the array's memory consumption.
_Symbol type:_ `function`
#### `fiobj_array_push`
```c
FIO_ARRAY_TYPE *fiobj_array_push(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Pushes an object to the end of the Array. Returns a pointer to the new object
or NULL on error.
_Symbol type:_ `function`
#### `fiobj_array_pop`
```c
int fiobj_array_pop(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE *old)
```
Removes an object from the end of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
_Symbol type:_ `function`
#### `fiobj_array_unshift`
```c
FIO_ARRAY_TYPE *fiobj_array_unshift(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE data)
```
Unshifts an object to the beginning of the Array. Returns a pointer to the
new object or NULL on error.
This could be expensive, causing `memmove`.
_Symbol type:_ `function`
#### `fiobj_array_shift`
```c
int fiobj_array_shift(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE *old)
```
Removes an object from the beginning of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
_Symbol type:_ `function`
#### `fiobj_array_each`
```c
uint32_t fiobj_array_each(FIO_ARRAY_PTR ary, int (*task)(FIO_NAME(FIO_ARRAY_NAME, each_s) * info), void *udata, int64_t start_at)
```
Iteration using a callback for each entry in the array.
The callback task function must accept an each_s pointer, see above.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
_Symbol type:_ `function`
#### `fiobj_array_each_next`
```c
inline FIO_ARRAY_TYPE *fiobj_array_each_next(FIO_ARRAY_PTR ary, FIO_ARRAY_TYPE **first, FIO_ARRAY_TYPE *pos)
```
Returns a pointer to the (next) object in the array.
Returns a pointer to the first object if `pos == NULL` and there are objects
in the array.
The first pointer is automatically set and it allows object insertions and
memory effecting functions to be called from within the loop.
If the object in `pos` (or an object before it) were removed, consider
passing `pos-1` to the function, to avoid skipping any elements while
looping.
Returns the next object if both `first` and `pos` are valid.
Returns NULL if `pos` was the last object or no object exist.
Returns the first object if either `first` or `pos` are invalid.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/210 map.h`
30 public symbols.
### Macros
#### `FIO_MAP_INIT`
```c
#define FIO_MAP_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_MAP_EACH`
```c
#define FIO_MAP_EACH(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_next)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_next)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
#### `FIO_MAP_EACH_REVERSED`
```c
#define FIO_MAP_EACH_REVERSED(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_prev)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_prev)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
### Types
#### `fiobj_hash_node_s`
```c
struct fiobj_hash_node_s
```
internal object data representation
_Symbol type:_ `type`
#### `fiobj_hash_s`
```c
struct fiobj_hash_s {
uint32_t bits;
uint32_t count;
FIO_NAME(FIO_MAP_NAME, node_s) * map;
#if FIO_MAP_ORDERED
FIO_INDEXED_LIST32_HEAD head;
#endif
}
```
A Hash Map / Set type
_Symbol type:_ `type`
#### `fiobj_hash_iterator_s`
```c
typedef struct {
/** the node in the internal map */
fiobj_hash_iterator_s * node;
/** the key in the current position */
FIO_MAP_KEY key;
#ifdef FIO_MAP_VALUE
/** the value in the current position */
FIO_MAP_VALUE value;
#endif
#if !FIO_MAP_RECALC_HASH
/** the hash for the current position */
uint64_t hash;
#endif
struct { /* internal usage, do not access */
uint32_t index; /* the index in the internal map */
uint32_t pos; /* the position in the ordering scheme */
uintptr_t map_validator; /* map mutation guard */
} private_;
} fiobj_hash_iterator_s
```
Map iterator type
_Symbol type:_ `type`
#### `fiobj_hash_each_s`
```c
struct fiobj_hash_each_s {
/** The being iterated. Once set, cannot be safely changed. */
FIO_MAP_PTR const parent;
/** The current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct FIO_NAME(FIO_MAP_NAME, each_s) * info);
/** Opaque user data. */
void *udata;
#ifdef FIO_MAP_VALUE
/** The object's value at the current index. */
FIO_MAP_VALUE value;
#endif
/** The object's key the current index. */
FIO_MAP_KEY key;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_hash_destroy`
```c
void fiobj_hash_destroy(FIO_MAP_PTR map)
```
Destroys the object, re-initializing its container.
_Symbol type:_ `function`
#### `fiobj_hash_capa`
```c
inline uint32_t fiobj_hash_capa(FIO_MAP_PTR map)
```
Theoretical map capacity.
_Symbol type:_ `function`
#### `fiobj_hash_count`
```c
inline uint32_t fiobj_hash_count(FIO_MAP_PTR map)
```
The number of objects in the map capacity.
_Symbol type:_ `function`
#### `fiobj_hash_reserve`
```c
void fiobj_hash_reserve(FIO_MAP_PTR map, size_t capa)
```
Reserves at minimum the capacity requested.
_Symbol type:_ `function`
#### `fiobj_hash_node2key`
```c
inline FIO_MAP_KEY fiobj_hash_node2key(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the key value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2hash`
```c
inline uint64_t fiobj_hash_node2hash(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the hash value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2val`
```c
inline FIO_MAP_VALUE fiobj_hash_node2val(FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2key_ptr`
```c
inline FIO_MAP_KEY_INTERNAL *fiobj_hash_node2key_ptr( FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the key value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_node2val_ptr`
```c
inline FIO_MAP_VALUE_INTERNAL *fiobj_hash_node2val_ptr( FIO_NAME(FIO_MAP_NAME, node_s) * node)
```
Returns the value associated with the node's pointer (see set_ptr).
_Symbol type:_ `function`
#### `fiobj_hash_remove`
```c
int fiobj_hash_remove(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key, #if defined(FIO_MAP_VALUE) FIO_MAP_VALUE_INTERNAL *old #else FIO_MAP_KEY_INTERNAL *old #endif )
```
Removes an object in the map, returning a pointer to the map data.
_Symbol type:_ `function`
#### `fiobj_hash_evict`
```c
void fiobj_hash_evict(FIO_MAP_PTR map, size_t number_of_elements)
```
Evicts elements in order least recently used (LRU), FIFO or undefined.
_Symbol type:_ `function`
#### `fiobj_hash_clear`
```c
void fiobj_hash_clear(FIO_MAP_PTR map)
```
Removes all objects from the map, without releasing the map's resources.
_Symbol type:_ `function`
#### `fiobj_hash_compact`
```c
void fiobj_hash_compact(FIO_MAP_PTR map)
```
Attempts to minimize memory use.
_Symbol type:_ `function`
#### `fiobj_hash_get`
```c
inline FIO_MAP_GET_T fiobj_hash_get(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key)
```
Gets a value from the map, if exists.
_Symbol type:_ `function`
#### `fiobj_hash_set`
```c
inline FIO_MAP_GET_T fiobj_hash_set(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif #ifdef FIO_MAP_VALUE FIO_MAP_KEY key, FIO_MAP_VALUE obj, FIO_MAP_VALUE_INTERNAL *old #else FIO_MAP_KEY key #endif )
```
Sets a value in the map, hash maps will overwrite existing data if any.
_Symbol type:_ `function`
#### `fiobj_hash_set_if_missing`
```c
inline FIO_MAP_GET_T fiobj_hash_set_if_missing(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key #ifdef FIO_MAP_VALUE , FIO_MAP_VALUE obj #endif )
```
Sets a value in the map if not set previously.
_Symbol type:_ `function`
#### `fiobj_hash_set_ptr`
```c
fiobj_hash_set_ptr * FIO_NAME(FIO_MAP_NAME, set_ptr)(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif #ifdef FIO_MAP_VALUE FIO_MAP_KEY key, FIO_MAP_VALUE val, FIO_MAP_VALUE_INTERNAL *old, int overwrite #else FIO_MAP_KEY key #endif )
```
The core set function.
This function returns `NULL` on error (errors are logged).
If the map is a hash map, overwriting the value (while keeping the key) is
possible. In this case the `old` pointer is optional, and if set than the old
data will be copied to over during an overwrite.
NOTE: the function returns a pointer to the map's internal storage.
_Symbol type:_ `function`
#### `fiobj_hash_get_ptr`
```c
fiobj_hash_get_ptr * FIO_NAME(FIO_MAP_NAME, get_ptr)(FIO_MAP_PTR map, #if !defined(FIO_MAP_HASH_FN) uint64_t hash, #endif FIO_MAP_KEY key)
```
The core get function. This function returns NULL if item is missing.
NOTE: the function returns a pointer to the map's internal storage.
_Symbol type:_ `function`
#### `fiobj_hash_each`
```c
uint32_t fiobj_hash_each(FIO_MAP_PTR map, int (*task)(FIO_NAME(FIO_MAP_NAME, each_s) *), void *udata, ssize_t start_at)
```
Iteration using a callback for each element in the map.
The callback task function must accept an each_s pointer, see above.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
_Symbol type:_ `function`
#### `fiobj_hash_get_next`
```c
fiobj_hash_get_next FIO_NAME(FIO_MAP_NAME, get_next)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * current_pos)
```
Returns the next iterator object after `current_pos` or the first if `NULL`.
Note that adding objects to the map or rehashing between iterations could
incur performance penalties when re-setting and re-seeking the previous
iterator position.
Adding objects to, or rehashing, an unordered maps could invalidate the
iterator object completely as the ordering may have changed and so the "next"
object might be any object in the map.
_Symbol type:_ `function`
#### `fiobj_hash_get_prev`
```c
fiobj_hash_get_prev FIO_NAME(FIO_MAP_NAME, get_prev)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * current_pos)
```
Returns the next iterator object after `current_pos` or the last if `NULL`.
See notes in `get_next`.
_Symbol type:_ `function`
#### `fiobj_hash_iterator_is_valid`
```c
inline int fiobj_hash_iterator_is_valid(FIO_NAME(FIO_MAP_NAME, iterator_s) * iterator)
```
Returns 1 if the iterator is out of bounds, otherwise returns 0.
_Symbol type:_ `function`
#### `fiobj_hash_iterator2node`
```c
inline fiobj_hash_iterator2node * FIO_NAME(FIO_MAP_NAME, iterator2node)(FIO_MAP_PTR map, FIO_NAME(FIO_MAP_NAME, iterator_s) * iterator)
```
Returns a pointer to the node object in the internal map.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/210 map2.h`
3 public symbols.
### Macros
#### `FIO_MAP2_INIT`
```c
#define FIO_MAP2_INIT \
{ 0 }
```
_Symbol type:_ `macro`
#### `FIO_MAP2_EACH`
```c
#define FIO_MAP2_EACH(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_next)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_next)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
#### `FIO_MAP2_EACH_REVERSED`
```c
#define FIO_MAP2_EACH_REVERSED(map_name, map_ptr, i) \
for (FIO_NAME(map_name, iterator_s) \
i = FIO_NAME(map_name, get_prev)(map_ptr, NULL); \
FIO_NAME(map_name, iterator_is_valid)(&i); \
i = FIO_NAME(map_name, get_prev)(map_ptr, &i))
```
Iterates through the map using an iterator object.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/249 reference counter.h`
17 public symbols.
### Functions
#### `fiobj_array_new`
```c
FIO_REF_TYPE_PTR fiobj_array_new(void)
```
_Symbol type:_ `function`
#### `fiobj_hash_new`
```c
FIO_REF_TYPE_PTR fiobj_hash_new(void)
```
_Symbol type:_ `function`
#### `fiobj_object_new`
```c
FIO_REF_TYPE_PTR fiobj_object_new(void)
```
_Symbol type:_ `function`
#### `fiobj_str_new`
```c
FIO_REF_TYPE_PTR fiobj_str_new(void)
```
_Symbol type:_ `function`
#### `fiobj_array_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_array_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_hash_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_hash_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_object_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_object_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_str_dup`
```c
inline FIO_REF_TYPE_PTR fiobj_str_dup(const FIO_REF_TYPE_PTR wrapped)
```
Increases the reference count.
_Symbol type:_ `function`
#### `fiobj_array_free`
```c
void fiobj_array_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_hash_free`
```c
void fiobj_hash_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_object_free`
```c
void fiobj_object_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_str_free`
```c
void fiobj_str_free(FIO_REF_TYPE_PTR wrapped)
```
Frees a reference counted object (or decreases the reference count).
_Symbol type:_ `function`
#### `fiobj_object_metadata`
```c
FIO_REF_METADATA *fiobj_object_metadata(FIO_REF_TYPE_PTR wrapped)
```
Returns a pointer to the object's metadata, if defined.
_Symbol type:_ `function`
#### `fiobj_array_references`
```c
inline size_t fiobj_array_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_hash_references`
```c
inline size_t fiobj_hash_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_object_references`
```c
inline size_t fiobj_object_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
#### `fiobj_str_references`
```c
inline size_t fiobj_str_references(FIO_REF_TYPE_PTR wrapped_)
```
Debugging helper, do not use for data, as returned value is unstable.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/250 fiobj.h`
66 public symbols.
### Macros
#### `FIOBJ_MAX_NESTING`
```c
#define FIOBJ_MAX_NESTING 512
```
Sets the limit on nesting level transversal by recursive functions.
This effects JSON output / input and the `fiobj_each2` function since they
are recursive.
HOWEVER: this value will NOT effect the recursive `fiobj_free` which could
(potentially) expload the stack if given melformed input such as cyclic data
structures.
Values should be less than 32K.
_Symbol type:_ `macro`
#### `FIOBJ_JSON_APPEND`
```c
#define FIOBJ_JSON_APPEND 1
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ALLOC`
```c
#define FIOBJ_MARK_MEMORY_ALLOC() \
fio_atomic_add(&FIOBJ_MARK_MEMORY_ALLOC_COUNTER, 1)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_FREE`
```c
#define FIOBJ_MARK_MEMORY_FREE() \
fio_atomic_add(&FIOBJ_MARK_MEMORY_FREE_COUNTER, 1)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_PRINT`
```c
#define FIOBJ_MARK_MEMORY_PRINT() \
FIO___LOG_PRINT_LEVEL( \
((FIOBJ_MARK_MEMORY_ALLOC_COUNTER == FIOBJ_MARK_MEMORY_FREE_COUNTER) \
? 4 /* FIO_LOG_LEVEL_INFO */ \
: 3 /* FIO_LOG_LEVEL_WARNING */), \
((FIOBJ_MARK_MEMORY_ALLOC_COUNTER == FIOBJ_MARK_MEMORY_FREE_COUNTER) \
? "INFO: total remaining FIOBJ allocations: %zu (%zu - %zu)" \
: "WARNING: LEAKED! FIOBJ allocations: %zu (%zu - %zu)"), \
FIOBJ_MARK_MEMORY_ALLOC_COUNTER - FIOBJ_MARK_MEMORY_FREE_COUNTER, \
FIOBJ_MARK_MEMORY_ALLOC_COUNTER, \
FIOBJ_MARK_MEMORY_FREE_COUNTER)
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ENABLED`
```c
#define FIOBJ_MARK_MEMORY_ENABLED 1
```
If true, FIOBJ memory allocation counting is enabled.
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_ALLOC_COUNTER`
```c
#define FIOBJ_MARK_MEMORY_ALLOC_COUNTER 0 /* when testing unmarked FIOBJ */
```
_Symbol type:_ `macro`
#### `FIOBJ_MARK_MEMORY_FREE_COUNTER`
```c
#define FIOBJ_MARK_MEMORY_FREE_COUNTER 0 /* when testing unmarked FIOBJ */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_NULL`
```c
#define FIOBJ_T_NULL 2 /* 0b010 a lonely second bit signifies a primitive */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_TRUE`
```c
#define FIOBJ_T_TRUE 18 /* 0b010 010 - primitive value */
```
_Symbol type:_ `macro`
#### `FIOBJ_T_FALSE`
```c
#define FIOBJ_T_FALSE 34 /* 0b100 010 - primitive value */
```
_Symbol type:_ `macro`
#### `FIOBJ_TYPE`
```c
#define FIOBJ_TYPE(o) fiobj_type(o)
```
Use the macros to avoid future API changes.
_Symbol type:_ `macro`
#### `FIOBJ_TYPE_IS`
```c
#define FIOBJ_TYPE_IS(o, type) (fiobj_type(o) == type)
```
Use the macros to avoid future API changes.
_Symbol type:_ `macro`
#### `FIOBJ_T_INVALID`
```c
#define FIOBJ_T_INVALID 0
```
Identifies an invalid type identifier (returned from FIOBJ_TYPE(o)
_Symbol type:_ `macro`
#### `FIOBJ_INVALID`
```c
#define FIOBJ_INVALID 0
```
Identifies an invalid object
_Symbol type:_ `macro`
#### `FIOBJ_IS_INVALID`
```c
#define FIOBJ_IS_INVALID(o) (((uintptr_t)(o)&7UL) == 0)
```
Tests if the object is (probably) a valid FIOBJ
_Symbol type:_ `macro`
#### `FIOBJ_IS_NULL`
```c
#define FIOBJ_IS_NULL(o) (FIOBJ_IS_INVALID(o) || ((o) == FIOBJ_T_NULL))
```
_Symbol type:_ `macro`
#### `FIOBJ_TYPE_CLASS`
```c
#define FIOBJ_TYPE_CLASS(o) ((fiobj_class_en)(((uintptr_t)(o)) & 7UL))
```
_Symbol type:_ `macro`
#### `FIOBJ_PTR_TAG`
```c
#define FIOBJ_PTR_TAG(o, klass) ((uintptr_t)(((uintptr_t)(o)) | (klass)))
```
_Symbol type:_ `macro`
#### `FIOBJ_PTR_UNTAG`
```c
#define FIOBJ_PTR_UNTAG(o) ((uintptr_t)(((uintptr_t)(o)) & (~7ULL)))
```
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR`
```c
#define FIOBJ_STR_TEMP_VAR(str_name) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, __auto_mem_tmp) = {0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR_STATIC`
```c
#define FIOBJ_STR_TEMP_VAR_STATIC(str_name, buf_, len_) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, \
__auto_mem_tmp) = {0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT_STATIC2((buf_), (len_))}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack, initialized with a
static string.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_VAR_EXISTING`
```c
#define FIOBJ_STR_TEMP_VAR_EXISTING(str_name, buf_, len_, capa_) \
struct { \
uint64_t i1; \
uint64_t i2; \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), s) s; \
} FIO_NAME(str_name, __auto_mem_tmp) = { \
0x7f7f7f7f7f7f7f7fULL, \
0x7f7f7f7f7f7f7f7fULL, \
FIO_STR_INIT_EXISTING((buf_), (len_), (capa_))}; \
FIOBJ str_name = \
(FIOBJ)(((uintptr_t) & (FIO_NAME(str_name, __auto_mem_tmp).s)) | \
FIOBJ_T_STRING);
```
Creates a temporary FIOBJ String object on the stack, initialized with a
static string.
String data might be allocated dynamically.
_Symbol type:_ `macro`
#### `FIOBJ_STR_TEMP_DESTROY`
```c
#define FIOBJ_STR_TEMP_DESTROY(str_name) \
FIO_NAME(FIO_NAME(fiobj, FIOBJ___NAME_STRING), destroy)(str_name);
```
Resets a temporary FIOBJ String, freeing and any resources allocated.
_Symbol type:_ `macro`
### Types
#### `fiobj_class_en`
```c
typedef enum {
FIOBJ_T_NUMBER = 0x01, /* 0b001 3 bits taken for small numbers */
FIOBJ_T_PRIMITIVE = 2, /* 0b010 a lonely second bit signifies a primitive */
FIOBJ_T_STRING = 3, /* 0b011 */
FIOBJ_T_ARRAY = 4, /* 0b100 */
FIOBJ_T_HASH = 5, /* 0b101 */
FIOBJ_T_FLOAT = 6, /* 0b110 */
FIOBJ_T_OTHER = 7, /* 0b111 dynamic type - test content */
} fiobj_class_en
```
FIOBJ type enum for common / primitive types.
_Symbol type:_ `type`
#### `fiobj_each_s`
```c
struct fiobj_each_s {
/** The being iterated. Once set, cannot be safely changed. */
FIOBJ const parent;
/** The index to start at / the current object's index */
uint64_t index;
/** The callback / task called for each index, may be updated mid-cycle. */
int (*task)(struct fiobj_each_s *info);
/** The argument passed along to the task. */
void *udata;
/** The value of the current object in the Array or Hash Map */
FIOBJ value;
/* The key, if a Hash Map */
FIOBJ key;
}
```
Iteration information structure passed to the callback.
_Symbol type:_ `type`
### Functions
#### `fiobj_type`
```c
inline size_t fiobj_type(FIOBJ o)
```
Returns an objects type. This isn't limited to known types.
_Symbol type:_ `function`
#### `fiobj_dup`
```c
inline FIOBJ fiobj_dup(FIOBJ o)
```
Increases an object's reference count (or copies) and returns it.
_Symbol type:_ `function`
#### `fiobj_free`
```c
inline void fiobj_free(FIOBJ o)
```
Decreases an object's reference count or frees it.
_Symbol type:_ `function`
#### `fiobj_is_eq`
```c
inline unsigned char fiobj_is_eq(FIOBJ a, FIOBJ b)
```
Compares two objects.
_Symbol type:_ `function`
#### `fiobj2cstr`
```c
inline fio_str_info_s fiobj2cstr(FIOBJ o)
```
Returns a temporary String representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2i`
```c
inline intptr_t fiobj2i(FIOBJ o)
```
Returns an integer representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2f`
```c
inline double fiobj2f(FIOBJ o)
```
Returns a float (double) representation for any FIOBJ object.
_Symbol type:_ `function`
#### `fiobj2hash`
```c
inline uint64_t fiobj2hash(FIOBJ object_key)
```
Calculates an object's hash value for a specific hash map object.
_Symbol type:_ `function`
#### `fiobj_each1`
```c
FIO_SFUNC uint32_t fiobj_each1(FIOBJ o, int (*task)(fiobj_each_s *info), void *udata, int32_t start_at)
```
Performs a task for each element held by the FIOBJ object.
If `task` returns -1, the `each` loop will break (stop).
Returns the "stop" position - the number of elements processed + `start_at`.
_Symbol type:_ `function`
#### `fiobj_each2`
```c
uint32_t fiobj_each2(FIOBJ o, int (*task)(fiobj_each_s *info), void *udata)
```
Performs a task for the object itself and each element held by the FIOBJ
object or any of it's elements (a deep task).
The order of performance is by order of appearance, as if all nesting levels
were flattened.
If `task` returns -1, the `each` loop will break (stop).
Returns the number of elements processed.
_Symbol type:_ `function`
#### `fiobj_true`
```c
inline FIOBJ fiobj_true(void)
```
Returns the `true` primitive.
_Symbol type:_ `function`
#### `fiobj_false`
```c
inline FIOBJ fiobj_false(void)
```
Returns the `false` primitive.
_Symbol type:_ `function`
#### `fiobj_null`
```c
inline FIOBJ fiobj_null(void)
```
Returns the `nil` / `null` primitive.
_Symbol type:_ `function`
#### `fiobj_num_new`
```c
inline FIOBJ FIO_NAME(fiobj_num_new, new)(intptr_t i)
```
Creates a new Number object.
_Symbol type:_ `function`
#### `fiobj_num2i`
```c
inline intptr_t FIO_NAME2(fiobj_num2i, i)(FIOBJ i)
```
Reads the number from a FIOBJ Number.
_Symbol type:_ `function`
#### `fiobj_num2f`
```c
inline double FIO_NAME2(fiobj_num2f, f)(FIOBJ i)
```
Reads the number from a FIOBJ Number, fitting it in a double.
_Symbol type:_ `function`
#### `fiobj_num2cstr`
```c
fio_str_info_s FIO_NAME2(fiobj_num2cstr, cstr)(FIOBJ i)
```
Returns a String representation of the number (in base 10).
_Symbol type:_ `function`
#### `fiobj_num_free`
```c
inline void FIO_NAME(fiobj_num_free, free)(FIOBJ i)
```
Frees a FIOBJ number.
_Symbol type:_ `function`
#### `fiobj_float_new`
```c
inline FIOBJ FIO_NAME(fiobj_float_new, new)(double i)
```
Creates a new Float (double) object.
_Symbol type:_ `function`
#### `fiobj_float2i`
```c
inline intptr_t FIO_NAME2(fiobj_float2i, i)(FIOBJ i)
```
Reads the number from a FIOBJ Float rounding it to an integer.
_Symbol type:_ `function`
#### `fiobj_float2f`
```c
inline double FIO_NAME2(fiobj_float2f, f)(FIOBJ i)
```
Reads the value from a FIOBJ Float, as a double.
_Symbol type:_ `function`
#### `fiobj_float2cstr`
```c
fio_str_info_s FIO_NAME2(fiobj_float2cstr, cstr)(FIOBJ i)
```
Returns a String representation of the float.
_Symbol type:_ `function`
#### `fiobj_float_free`
```c
inline void FIO_NAME(fiobj_float_free, free)(FIOBJ i)
```
Frees a FIOBJ Float.
_Symbol type:_ `function`
#### `fiobj_str_new_cstr`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_cstr, new_cstr)(const char *ptr, size_t len)
```
_Symbol type:_ `function`
#### `fiobj_str_new_buf`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_buf, new_buf)(size_t capa)
```
_Symbol type:_ `function`
#### `fiobj_str_new_copy`
```c
inline FIOBJ FIO_NAME(fiobj_str_new_copy, new_copy)(FIOBJ original)
```
_Symbol type:_ `function`
#### `fiobj_str2cstr`
```c
inline fio_str_info_s FIO_NAME2(fiobj_str2cstr, cstr)(FIOBJ s)
```
Returns information about the string. Same as fiobj_str_info().
_Symbol type:_ `function`
#### `fiobj_hash_set2`
```c
inline FIOBJ FIO_NAME(fiobj_hash_set2, set2)(FIOBJ hash, const char *key, size_t len, FIOBJ value)
```
Sets a value in a hash map, allocating the key String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj_hash_get2`
```c
inline FIOBJ FIO_NAME(fiobj_hash_get2, get2)(FIOBJ hash, const char *buf, size_t len)
```
Finds a value in the hash map, using a temporary String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj_hash_remove2`
```c
inline int FIO_NAME(fiobj_hash_remove2, remove2)(FIOBJ hash, const char *buf, size_t len, FIOBJ *old)
```
Removes a value in a hash map, using a temporary String and automatically
calculating the hash value.
_Symbol type:_ `function`
#### `fiobj2json`
```c
inline FIOBJ fiobj2json(FIOBJ dest, FIOBJ o, uint8_t beautify)
```
Returns a JSON valid FIOBJ String, representing the object.
If `dest` is an existing String, the formatted JSON data will be appended to
the existing string.
_Symbol type:_ `function`
#### `fiobj_hash_update_json`
```c
size_t FIO_NAME(fiobj_hash_update_json, update_json)(FIOBJ hash, fio_str_info_s str)
```
Updates a Hash using JSON data.
Parsing errors and non-dictionary object JSON data are silently ignored,
attempting to update the Hash as much as possible before any errors
encountered.
Conflicting Hash data is overwritten (preferring the new over the old).
Returns the number of bytes consumed. On Error, 0 is returned and no data is
consumed.
_Symbol type:_ `function`
#### `fiobj_hash_update_json2`
```c
inline size_t FIO_NAME(fiobj_hash_update_json2, update_json2)(FIOBJ hash, char *ptr, size_t len)
```
Helper function, calls `fiobj_hash_update_json` with string information
_Symbol type:_ `function`
#### `fiobj_json_parse`
```c
FIOBJ fiobj_json_parse(fio_str_info_s str, size_t *consumed)
```
Parses a C string for JSON data. If `consumed` is not NULL, the `size_t`
variable will contain the number of bytes consumed before the parser stopped
(due to either error or end of a valid JSON data segment).
Returns a FIOBJ object matching the JSON valid C string `str`.
If the parsing failed (no complete valid JSON data) `FIOBJ_INVALID` is
returned.
_Symbol type:_ `function`
#### `fiobj_json_parse2`
```c
#define fiobj_json_parse2(data_, len_, consumed) \
fiobj_json_parse(FIO_STR_INFO2(data_, len_), consumed)
```
Helper macro, calls `fiobj_json_parse` with string information
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fiobj_json_find`
```c
FIOBJ fiobj_json_find(FIOBJ object, fio_str_info_s notation)
```
Uses JavaScript style notation to find data in an object structure.
For example, "[0].name" will return the "name" property of the first object
in an array object.
Returns a temporary reference to the object or FIOBJ_INVALID on an error.
Use `fiobj_dup` to collect an actual reference to the returned object.
_Symbol type:_ `function`
#### `fiobj_json_find2`
```c
#define fiobj_json_find2(object, str, length) \
fiobj_json_find(object, FIO_STR_INFO2(str, length))
```
Uses JavaScript style notation to find data in an object structure.
For example, "[0].name" will return the "name" property of the first object
in an array object.
Returns a temporary reference to the object or FIOBJ_INVALID on an error.
Use `fiobj_dup` to collect an actual reference to the returned object.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fiobj_mustache_build`
```c
inline FIOBJ fiobj_mustache_build(fio_mustache_s *m, FIOBJ ctx)
```
Builds a Mustache template using a FIOBJ context (usually a Hash).
Returns a FIOBJ String with the rendered template. May return `FIOBJ_INVALID`
if nothing was written.
_Symbol type:_ `function`
#### `fiobj_mustache_build2`
```c
inline FIOBJ fiobj_mustache_build2(fio_mustache_s *m, FIOBJ dest, FIOBJ ctx)
```
Builds a Mustache template using a FIOBJ context (usually a Hash).
Writes output to `dest` string (may be `FIOBJ_INVALID` / `NULL`).
Returns `dest` (or a new String). May return `FIOBJ_INVALID` if nothing was
written and `dest` was empty.
_Symbol type:_ `function`
#### `fiobj_hash_update`
```c
inline void FIO_NAME(fiobj_hash_update, update)(FIOBJ dest, FIOBJ src)
```
Updates a hash using information from another Hash.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/401 io api.h`
104 public symbols.
### Macros
#### `FIO_IO_BUFFER_PER_WRITE`
```c
#define FIO_IO_BUFFER_PER_WRITE 65536U
```
Control the size of the on-stack buffer used for `write` events.
_Symbol type:_ `macro`
#### `FIO_IO_THROTTLE_LIMIT`
```c
#define FIO_IO_THROTTLE_LIMIT 2097152U
```
IO will be throttled (no `on_data` events) if outgoing buffer is large.
_Symbol type:_ `macro`
#### `FIO_IO_TIMEOUT_MAX`
```c
#define FIO_IO_TIMEOUT_MAX 300000
```
Controls the maximum and default timeout in milliseconds (5 minutes).
_Symbol type:_ `macro`
#### `FIO_IO_SHUTDOWN_TIMEOUT`
```c
#define FIO_IO_SHUTDOWN_TIMEOUT 15000
```
_Symbol type:_ `macro`
#### `FIO_IO_COUNT_STORAGE`
```c
#define FIO_IO_COUNT_STORAGE 1
```
_Symbol type:_ `macro`
#### `FIO_IO_ASYN_INIT`
```c
#define FIO_IO_ASYN_INIT \
{ 0 }
```
Initializes an IO Async Queue (multi-threaded task queue).
The queue automatically spawns threads and shuts down as the IO reactor
starts or stops.
It is recommended that the `fio_io_async_s` be allocated as a static
variable, as its memory must remain valid throughout the lifetime of the
IO reactor's app.
_Symbol type:_ `macro`
### Types
#### `fio_io_protocol_s`
```c
struct fio_io_protocol_s
```
The main protocol object type. See `struct fio_io_protocol_s`.
_Symbol type:_ `type`
#### `fio_io_functions_s`
```c
struct fio_io_functions_s
```
The IO functions used by the protocol object.
_Symbol type:_ `type`
#### `fio_io_s`
```c
struct fio_io_s
```
The main IO object type. Should be treated as an opaque pointer.
_Symbol type:_ `type`
#### `fio_io_tls_s`
```c
struct fio_io_tls_s
```
An opaque type used for the SSL/TLS helper functions.
_Symbol type:_ `type`
#### `fio_pubsub_msg_s`
```c
struct fio_pubsub_msg_s
```
Message structure, as received by the `on_message` subscription callback.
_Symbol type:_ `type`
#### `fio_io_async_s`
```c
struct fio_io_async_s
```
The IO Async Queue type.
_Symbol type:_ `type`
#### `fio_io_listen_args_s`
```c
struct fio_io_listen_args_s {
/**
* The binding address in URL format. Defaults to: tcp://0.0.0.0:3000
*
* Note: `.url` accept an optional query for building a TLS context.
*
* Possible query values include:
*
* - `tls` or `ssl` (no value): sets TLS as active, possibly self-signed.
* - `tls=` or `ssl=`: value is a prefix for "key.pem" and "cert.pem".
* - `key=` and `cert=`: file paths for ".pem" files.
*
* i.e.:
*
* fio_io_listen(.url = "0.0.0.0:3000/?tls", ...);
* fio_io_listen(.url = "0.0.0.0:3000/?tls=./", ...);
* // same as:
* fio_io_listen(.url = "0.0.0.0:3000/"
* "?key=./key.pem"
* "&cert=./cert.pem", ...);
*/
const char *url;
/** The `fio_io_protocol_s` that will be assigned to incoming
* connections. */
fio_io_protocol_s *protocol;
/** The default `udata` set for (new) incoming connections. */
void *udata;
/** TLS object used for incoming connections (ownership moved to listener). */
fio_io_tls_s *tls;
/**
* Called when the a listening socket starts to listen.
*
* May be called multiple times (i.e., if the IO reactor stops and restarts).
*/
void (*on_start)(fio_io_protocol_s *protocol, void *udata);
/**
* Called during listener cleanup.
*
* This will be called separately for every process before exiting.
*/
void (*on_stop)(fio_io_protocol_s *protocol, void *udata);
/**
* Selects a queue that will be used to schedule a pre-accept task.
* May be used to test user thread stress levels before accepting connections.
*/
fio_io_async_s *queue_for_accept;
/** When forking the IO reactor - limits `listen` to the root process. */
uint8_t on_root;
/** Hides "started/stopped listening" messages from log (if set). */
uint8_t hide_from_log;
}
```
Arguments for the fio_io_listen function
_Symbol type:_ `type`
#### `fio_io_listener_s`
```c
struct fio_io_listener_s
```
_Symbol type:_ `type`
#### `fio_io_connect_args_s`
```c
typedef struct {
/** The URL to connect to (may contain TLS hints in query / `tls` scheme). */
const char *url;
/** Connection protocol (once connection established). */
fio_io_protocol_s *protocol;
/** Called in case of a failed connection, use for cleanup.
* Always runs on the IO thread, possibly asynchronously after
* `fio_io_connect` returns (even when it returns `NULL`). */
void (*on_failed)(fio_io_protocol_s *protocol, void *udata);
/** Opaque user data (set only once connection was established). */
void *udata;
/** TLS builder object for TLS connections. */
fio_io_tls_s *tls;
/** Connection timeout in milliseconds (defaults to 30 seconds). */
uint32_t timeout;
} fio_io_connect_args_s
```
Named arguments for fio_io_connect
_Symbol type:_ `type`
#### `fio_io_write_args_s`
```c
typedef struct {
/** The buffer with the data to send (if no file descriptor) */
void *buf;
/** The file descriptor to send (if no buffer) */
intptr_t fd;
/** The length of the data to be sent. On files, 0 = the whole file. */
size_t len;
/** The length of the data to be sent. On files, 0 = the whole file. */
size_t offset;
/**
* If this is a buffer, the de-allocation function used to free it.
*
* If `copy == 0`, and `dealloc` is set, `write` will take ownership.
*
* If NULL, the buffer will NOT be de-allocated.
*/
void (*dealloc)(void *);
/** If non-zero, makes a copy of the buffer or keeps a file open. */
uint8_t copy;
} fio_io_write_args_s
```
_Symbol type:_ `type`
#### `fio_io_env_set_args_s`
```c
typedef struct {
/** A numerical type filter. Defaults to 0. Negative values are reserved. */
intptr_t type;
/** The name for the link. The name and type uniquely identify the object. */
fio_buf_info_s name;
/** The object being linked to the connection. */
void *udata;
/** A callback that will be called once the connection is closed. */
void (*on_close)(void *data);
/** Set to true (1) if the name string's life lives as long as the `env` . */
uint8_t const_name;
} fio_io_env_set_args_s
```
Named arguments for the `fio_io_env_set` function.
_Symbol type:_ `type`
#### `fio_io_env_get_args_s`
```c
typedef struct {
/** A numerical type filter. Should be the same as used with
* `fio_io_env_set` */
intptr_t type;
/** The name of the object. Should be the same as used with `fio_io_env_set`
*/
fio_buf_info_s name;
} fio_io_env_get_args_s
```
Named arguments for the `fio_io_env_unset` function.
_Symbol type:_ `type`
#### `fio_io_tls_each_s`
```c
struct fio_io_tls_each_s {
fio_io_tls_s *tls;
void *udata;
void *udata2;
int (*each_cert)(struct fio_io_tls_each_s *,
const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *pk_password);
int (*each_alpn)(struct fio_io_tls_each_s *,
const char *protocol_name,
void (*on_selected)(fio_io_s *));
int (*each_trust)(struct fio_io_tls_each_s *, const char *public_cert_file);
}
```
Arguments (and info) for `fio_io_tls_each`.
_Symbol type:_ `type`
### Functions
#### `fio_io_stop`
```c
void fio_io_stop(void)
```
Stopping the IO reactor.
_Symbol type:_ `function`
#### `fio_io_add_workers`
```c
void fio_io_add_workers(int workers)
```
Adds `workers` amount of workers to the root IO reactor process.
_Symbol type:_ `function`
#### `fio_io_start`
```c
void fio_io_start(int workers)
```
Starts the IO reactor, using optional `workers` processes. Will BLOCK!
_Symbol type:_ `function`
#### `fio_io_restart`
```c
void fio_io_restart(int workers)
```
Retiers all existing workers and restarts with the number of workers.
_Symbol type:_ `function`
#### `fio_io_restart_on_signal`
```c
void fio_io_restart_on_signal(int signal)
```
Sets a signal to listen to for a hot restart (see `fio_io_restart`).
_Symbol type:_ `function`
#### `fio_io_shutdown_timeout`
```c
size_t fio_io_shutdown_timeout(void)
```
Returns the shutdown timeout for the reactor.
_Symbol type:_ `function`
#### `fio_io_shutdown_timeout_set`
```c
size_t fio_io_shutdown_timeout_set(size_t milliseconds)
```
Sets the shutdown timeout for the reactor, returning the new value.
_Symbol type:_ `function`
#### `fio_io_is_running`
```c
int fio_io_is_running(void)
```
Returns true if IO reactor running and 0 if stopped or shutting down.
_Symbol type:_ `function`
#### `fio_io_is_master`
```c
int fio_io_is_master(void)
```
Returns true if the current process is the IO reactor's master process.
_Symbol type:_ `function`
#### `fio_io_is_worker`
```c
int fio_io_is_worker(void)
```
Returns true if the current process is an IO reactor's worker process.
_Symbol type:_ `function`
#### `fio_io_workers`
```c
uint16_t fio_io_workers(int workers_requested)
```
Returns the number or workers the IO reactor will actually run.
_Symbol type:_ `function`
#### `fio_io_pid`
```c
int fio_io_pid(void)
```
Returns current process id.
_Symbol type:_ `function`
#### `fio_io_root_pid`
```c
int fio_io_root_pid(void)
```
Returns the root / master process id.
_Symbol type:_ `function`
#### `fio_io_last_tick`
```c
int64_t fio_io_last_tick(void)
```
Returns the last millisecond when the IO reactor polled for events.
_Symbol type:_ `function`
#### `fio_io_last_tick_time`
```c
int64_t fio_io_last_tick_time(void)
```
Returns a cached real-time (wall-clock) timestamp in milliseconds,
updated each IO tick alongside `fio_io_last_tick`. Use this to avoid
repeated `clock_gettime` syscalls when an approximate wall-clock ms is
sufficient (e.g., HTTP Date headers, access-log timestamps).
_Symbol type:_ `function`
#### `fio_io_listen`
```c
fio_io_listener_s *fio_io_listen(fio_io_listen_args_s args)
```
Sets up a network service on a listening socket.
Returns a self-destructible listener handle on success or NULL on error.
NOTE: this schedules a task and should NOT be called within a PRE_START or
ON_START state callback.
NOTE: the returned listener is valid until `fio_io_listen_stop` is called.
_Symbol type:_ `function`
#### `fio_io_listen`
```c
#define fio_io_listen(...) fio_io_listen((fio_io_listen_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_listen_stop`
```c
void fio_io_listen_stop(fio_io_listener_s *l)
```
Notifies a listener to stop listening and destroys it.
_Symbol type:_ `function`
#### `fio_io_listener_protocol`
```c
fio_io_protocol_s *fio_io_listener_protocol(fio_io_listener_s *l)
```
Returns the listener's associated protocol.
_Symbol type:_ `function`
#### `fio_io_listener_udata`
```c
void *fio_io_listener_udata(fio_io_listener_s *l)
```
Returns the listener's associated `udata`.
_Symbol type:_ `function`
#### `fio_io_listener_udata_set`
```c
void *fio_io_listener_udata_set(fio_io_listener_s *l, void *new_udata)
```
Sets the listener's associated `udata`, returning the old value.
_Symbol type:_ `function`
#### `fio_io_listener_url`
```c
fio_buf_info_s fio_io_listener_url(fio_io_listener_s *l)
```
Returns the URL on which the listener is listening.
_Symbol type:_ `function`
#### `fio_io_listener_is_tls`
```c
int fio_io_listener_is_tls(fio_io_listener_s *l)
```
Returns true if the listener protocol has an attached TLS context.
_Symbol type:_ `function`
#### `fio_io_connect`
```c
fio_io_s *fio_io_connect(fio_io_connect_args_s args)
```
Connects to a specific URL, returning the `fio_io_s` IO object or `NULL`.
Note: The IO object returned is owned by the reactor. The copy returned is
valid only until the next event is processed.
Ownership: calling `fio_io_connect` transfers ownership of `protocol`,
`udata` and the connecting state to the reactor. Cleanup of these MUST be
performed only from `on_failed` (connection never established) or
`on_close` (established connection closed) - never in response to
`fio_io_connect` returning `NULL`, as the reactor retains the connecting
state until the deferred failure task runs.
`on_failed` always runs on the IO thread and may fire asynchronously,
after `fio_io_connect` returns (including when `NULL` is returned). This
implies the reactor MUST run at some point after a call, or the
transferred ownership is never released.
_Symbol type:_ `function`
#### `fio_io_connect`
```c
#define fio_io_connect(url_, ...) \
fio_io_connect((fio_io_connect_args_s){.url = url_, __VA_ARGS__})
```
Connects to a specific URL, returning the `fio_io_s` IO object or `NULL`.
Note: The IO object returned is owned by the reactor. The copy returned is
valid only until the next event is processed.
Ownership: calling `fio_io_connect` transfers ownership of `protocol`,
`udata` and the connecting state to the reactor. Cleanup of these MUST be
performed only from `on_failed` (connection never established) or
`on_close` (established connection closed) - never in response to
`fio_io_connect` returning `NULL`, as the reactor retains the connecting
state until the deferred failure task runs.
`on_failed` always runs on the IO thread and may fire asynchronously,
after `fio_io_connect` returns (including when `NULL` is returned). This
implies the reactor MUST run at some point after a call, or the
transferred ownership is never released.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_attach_fd`
```c
fio_io_s *fio_io_attach_fd(fio_socket_i fd, fio_io_protocol_s *protocol, void *udata, void *tls)
```
Attaches the socket in `fd` to the facio.io engine (reactor).
* `fd` should point to a valid socket.
* `protocol` may be the existing protocol or NULL (for partial hijack).
* `udata` is opaque user data and may be any value, including NULL.
* `tls` is a context for Transport Layer (Security) and can be used to
redirect read/write operations, as set by the protocol.
Returns NULL on error. the `fio_io_s` pointer must NOT be used except
within proper callbacks.
Ownership of `tls` transfers to the reactor when `fio_io_attach_fd` is
called, whether the call succeeds or fails - callers must not use or free
it afterwards. On failure the reactor releases it with the TLS
implementation's `free_context` (never the per-connection `cleanup`).
_Symbol type:_ `function`
#### `fio_io_protocol_set`
```c
fio_io_protocol_s *fio_io_protocol_set(fio_io_s *io, fio_io_protocol_s *protocol)
```
Sets a new protocol object. `NULL` is a valid "only-write" protocol.
_Symbol type:_ `function`
#### `fio_io_protocol`
```c
fio_io_protocol_s *fio_io_protocol(fio_io_s *io)
```
Returns a pointer to the current protocol object.
If `protocol` wasn't properly set, the pointer might be NULL or invalid.
If `protocol` wasn't attached yet, may return the previous protocol.
_Symbol type:_ `function`
#### `fio_io_buffer`
```c
void *fio_io_buffer(fio_io_s *io)
```
Returns the a pointer to the memory buffer required by the protocol.
_Symbol type:_ `function`
#### `fio_io_buffer_len`
```c
size_t fio_io_buffer_len(fio_io_s *io)
```
Returns the length of the `buf` buffer.
_Symbol type:_ `function`
#### `fio_io_udata_set`
```c
void *fio_io_udata_set(fio_io_s *io, void *udata)
```
Associates a new `udata` pointer with the IO, returning the old `udata`
_Symbol type:_ `function`
#### `fio_io_udata`
```c
void *fio_io_udata(fio_io_s *io)
```
Returns the `udata` pointer associated with the IO.
_Symbol type:_ `function`
#### `fio_io_tls_set`
```c
void *fio_io_tls_set(fio_io_s *io, void *tls)
```
Associates a new `tls` pointer with the IO, returning the old `tls`
_Symbol type:_ `function`
#### `fio_io_tls`
```c
void *fio_io_tls(fio_io_s *io)
```
Returns the `tls` pointer associated with the IO.
_Symbol type:_ `function`
#### `fio_io_fd`
```c
fio_socket_i fio_io_fd(fio_io_s *io)
```
Returns the socket file descriptor (fd) associated with the IO.
_Symbol type:_ `function`
#### `fio_io_peer_info_next`
```c
int fio_io_peer_info_next(fio_io_s *io, fio_x509_cert_s *dest)
```
Returns the next peer information item for the connection.
Iterate to inspect the peer's certificate chain (leaf certificate first),
e.g., for client certificate authentication / authorization:
fio_x509_cert_s cert = {0}; // zeroed = new loop
while (fio_io_peer_info_next(io, &cert) == 0) {
// cert.verified != 0 if the TLS backend verified the chain
// cert.chain_index == position in the chain (0 == leaf)
// cert.cn, cert.fingerprint, etc.
}
The iterator is stateless — the position is identified from `dest` alone:
a zeroed `dest` (`der.buf == NULL`) starts a new loop; otherwise iteration
continues at `dest->chain_index + 1`. Multiple loops may iterate the same
connection concurrently without interfering with each other.
Iteration is capped at 128 certificates (`chain_index` 0..127) as a
deep-nesting / DoS guard — longer chains end the loop with -1.
A `NULL` `dest` returns -1. To restart a loop, zero the struct (e.g.,
`FIO_MEMSET(&cert, 0, sizeof(cert))`).
Returns 0 while data is available, or -1 when done / unavailable (e.g.,
handshake incomplete, the peer sent no certificate, or the X509 module is
unavailable).
NOTE: the parsed fields point into memory owned by the TLS backend and
remain valid only until the next fio_io_peer_info_next call (on ANY
connection) or until the connection is closed, whichever comes first.
_Symbol type:_ `function`
#### `fio_io_touch`
```c
void fio_io_touch(fio_io_s *io)
```
Resets a socket's timeout counter.
_Symbol type:_ `function`
#### `fio_io_read`
```c
size_t fio_io_read(fio_io_s *io, void *buf, size_t len)
```
Reads data to the buffer, if any data exists. Returns the number of bytes
read.
NOTE: zero (`0`) is a valid return value meaning no data was available.
_Symbol type:_ `function`
#### `fio_io_write2`
```c
void fio_io_write2(fio_io_s *io, fio_io_write_args_s args)
```
Writes data to the outgoing buffer and schedules the buffer to be sent.
_Symbol type:_ `function`
#### `fio_io_write2`
```c
#define fio_io_write2(io, ...) \
fio_io_write2(io, (fio_io_write_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_write`
```c
#define fio_io_write(io, buf_, len_) \
fio_io_write2(io, .buf = (buf_), .len = (len_), .copy = 1)
```
Helper macro for a common fio_io_write2 (copies the buffer).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_sendfile`
```c
#define fio_io_sendfile(io, source_fd, offset_, bytes) \
fio_io_write2((io), \
.fd = (source_fd), \
.offset = (size_t)(offset_), \
.len = (bytes))
```
Sends data from a file as if it were a single atomic packet (sends up to
length bytes or until EOF is reached).
Once the file was sent, the `source_fd` will be closed using `close`.
The file will be buffered to the socket chunk by chunk, so that memory
consumption is capped.
`offset` dictates the starting point for the data to be sent and length sets
the maximum amount of data to be sent.
Closes the file on error.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_close`
```c
void fio_io_close(fio_io_s *io)
```
Marks the IO for closure as soon as scheduled data was sent.
_Symbol type:_ `function`
#### `fio_io_close_now`
```c
void fio_io_close_now(fio_io_s *io)
```
Marks the IO for immediate closure and destruction.
_Symbol type:_ `function`
#### `fio_io_dup`
```c
fio_io_s *fio_io_dup(fio_io_s *io)
```
Increases a IO's reference count, so it won't be automatically destroyed
when all tasks have completed.
Use this function in order to use the IO outside of a scheduled task.
This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_free`
```c
void fio_io_free(fio_io_s *io)
```
Decreases a IO's reference count, so it could be automatically destroyed
when all other tasks have completed.
Use this function once finished with a IO that was `dup`-ed.
This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_suspend`
```c
void fio_io_suspend(fio_io_s *io)
```
Suspends future `on_data` events for the IO.
_Symbol type:_ `function`
#### `fio_io_unsuspend`
```c
void fio_io_unsuspend(fio_io_s *io)
```
Listens for future `on_data` events related to the IO.
_Symbol type:_ `function`
#### `fio_io_is_suspended`
```c
int fio_io_is_suspended(fio_io_s *io)
```
Returns 1 if the IO handle was suspended.
_Symbol type:_ `function`
#### `fio_io_on_data_schedule`
```c
void fio_io_on_data_schedule(fio_io_s *io)
```
Schedules one deferred `on_data` callback for already-buffered input.
Repeated calls before delivery are coalesced. This does not bypass normal
suspension or throttling checks.
_Symbol type:_ `function`
#### `fio_io_is_open`
```c
int fio_io_is_open(fio_io_s *io)
```
Returns 1 if the IO handle is open and not marked for closure.
_Symbol type:_ `function`
#### `fio_io_backlog`
```c
size_t fio_io_backlog(fio_io_s *io)
```
Returns the approximate number of bytes in the outgoing buffer.
_Symbol type:_ `function`
#### `fio_io_noop`
```c
void fio_io_noop(fio_io_s *io)
```
Does nothing.
_Symbol type:_ `function`
#### `fio_io_defer`
```c
void fio_io_defer(void (*task)(void *, void *), void *udata1, void *udata2)
```
Schedules a task for delayed execution. This function is thread-safe.
_Symbol type:_ `function`
#### `fio_io_run_every`
```c
void fio_io_run_every(fio_timer_schedule_args_s args)
```
Schedules a timer bound task, see `fio_timer_schedule`.
_Symbol type:_ `function`
#### `fio_io_run_every`
```c
#define fio_io_run_every(...) \
fio_io_run_every((fio_timer_schedule_args_s){__VA_ARGS__})
```
Schedules a timer bound task, see `fio_timer_schedule`.
Possible "named arguments" (fio_timer_schedule_args_s members) include:
* The timer function. If it returns a non-zero value, the timer stops:
int (*fn)(void *, void *)
* Opaque user data:
void *udata1
* Opaque user data:
void *udata2
* Called when the timer is done (finished):
void (*on_stop)(void *, void *)
* Timer interval, in milliseconds:
uint32_t every
* The number of times the timer should be performed. -1 == infinity:
int32_t repetitions
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_queue`
```c
fio_queue_s *fio_io_queue(void)
```
Returns a pointer for the IO reactor's queue.
_Symbol type:_ `function`
#### `fio_io_protocol_each`
```c
size_t fio_io_protocol_each(fio_io_protocol_s *protocol, void (*task)(fio_io_s *, void *udata2), void *udata2)
```
Performs a task for each IO in the stated protocol.
Call ONLY from the main IO thread (consider using `fio_io_defer`).
_Symbol type:_ `function`
#### `fio_io_env_get`
```c
void *fio_io_env_get(fio_io_s *io, fio_io_env_get_args_s)
```
Returns the named `udata` associated with the IO object (or `NULL`).
_Symbol type:_ `function`
#### `fio_io_env_get`
```c
#define fio_io_env_get(io, ...) \
fio_io_env_get(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Returns the named `udata` associated with the IO object (or `NULL`).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_set`
```c
void fio_io_env_set(fio_io_s *io, fio_io_env_set_args_s)
```
Links an object to a connection's lifetime / environment.
The `on_close` callback will be called once the connection has died.
If the `io` is NULL, the value will be set for the global environment.
_Symbol type:_ `function`
#### `fio_io_env_set`
```c
#define fio_io_env_set(io, ...) \
fio_io_env_set(io, (fio_io_env_set_args_s){__VA_ARGS__})
```
Links an object to a connection's lifetime, calling the `on_close` callback
once the connection has died.
If the `io` is NULL, the value will be set for the global environment, in
which case the `on_close` callback will only be called once the process
exits.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_unset`
```c
int fio_io_env_unset(fio_io_s *io, fio_io_env_get_args_s)
```
Un-links an object from the connection's lifetime, so it's `on_close`
callback will NOT be called.
Returns 0 on success and -1 if the object couldn't be found.
_Symbol type:_ `function`
#### `fio_io_env_unset`
```c
#define fio_io_env_unset(io, ...) \
fio_io_env_unset(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Un-links an object from the connection's lifetime, so it's `on_close`
callback will NOT be called.
Returns 0 on success and -1 if the object couldn't be found.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_env_remove`
```c
int fio_io_env_remove(fio_io_s *io, fio_io_env_get_args_s)
```
Removes an object from the connection's lifetime / environment, calling it's
`on_close` callback as if the connection was closed.
_Symbol type:_ `function`
#### `fio_io_env_remove`
```c
#define fio_io_env_remove(io, ...) \
fio_io_env_remove(io, (fio_io_env_get_args_s){__VA_ARGS__})
```
Removes an object from the connection's lifetime / environment, calling it's
`on_close` callback as if the connection was closed.
This is a helper MACRO that allows the function to be called using named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_tls_new`
```c
fio_io_tls_s *fio_io_tls_new(void)
```
Performs a `new` operation, returning a new `fio_io_tls_s` context.
_Symbol type:_ `function`
#### `fio_io_tls_from_url`
```c
fio_io_tls_s *fio_io_tls_from_url(fio_io_tls_s *target_or_null, fio_url_s url)
```
Takes a parsed URL and optional TLS target and returns a TLS if needed.
_Symbol type:_ `function`
#### `fio_io_tls_dup`
```c
fio_io_tls_s *fio_io_tls_dup(fio_io_tls_s *)
```
Performs a `dup` operation, increasing the object's reference count.
_Symbol type:_ `function`
#### `fio_io_tls_free`
```c
void fio_io_tls_free(fio_io_tls_s *)
```
Performs a `free` operation, reducing the reference count and freeing.
_Symbol type:_ `function`
#### `fio_io_tls_cert_add`
```c
fio_io_tls_s *fio_io_tls_cert_add(fio_io_tls_s *, const char *server_name, const char *public_cert_file, const char *private_key_file, const char *pk_password)
```
Adds a certificate a new SSL/TLS context / settings object (SNI support).
fio_io_tls_cert_add(tls, "www.example.com",
"public_key.pem",
"private_key.pem", NULL );
NOTE: Except for the `tls` and `server_name` arguments, all arguments might
be `NULL`, which a context builder (`fio_io_functions_s`) should
treat as a request for a self-signed certificate. It may be silently ignored.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_add`
```c
fio_io_tls_s *fio_io_tls_alpn_add(fio_io_tls_s *tls, const char *protocol_name, void (*on_selected)(fio_io_s *))
```
Adds an ALPN protocol callback to the SSL/TLS context.
The first protocol added will act as the default protocol to be selected.
A `NULL` protocol name will be silently ignored.
A `NULL` callback (`on_selected`) will be silently replaced with a no-op.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_select`
```c
int fio_io_tls_alpn_select(fio_io_tls_s *tls, const char *protocol_name, size_t name_length, fio_io_s *)
```
Calls the `on_selected` callback for the `fio_io_tls_s` object.
_Symbol type:_ `function`
#### `fio_io_tls_trust_add`
```c
fio_io_tls_s *fio_io_tls_trust_add(fio_io_tls_s *, const char *public_cert_file)
```
Adds a certificate to the "trust" list, which automatically adds a peer
verification requirement.
If `public_cert_file` is `NULL`, implementation is expected to add the
system's default trust registry.
Note: when the `fio_io_tls_s` object is used for server connections, this
should limit connections to clients that connect using a trusted certificate.
fio_io_tls_trust_add(tls, "google-ca.pem" );
_Symbol type:_ `function`
#### `fio_io_tls_cert_count`
```c
uintptr_t fio_io_tls_cert_count(fio_io_tls_s *tls)
```
Returns the number of `fio_io_tls_cert_add` instructions.
This could be used when deciding if to add a NULL instruction (self-signed).
If `fio_io_tls_cert_add` was never called, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_alpn_count`
```c
uintptr_t fio_io_tls_alpn_count(fio_io_tls_s *tls)
```
Returns the number of registered ALPN protocol names.
This could be used when deciding if protocol selection should be delegated to
the ALPN mechanism, or whether a protocol should be immediately assigned.
If no ALPN protocols are registered, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_trust_count`
```c
uintptr_t fio_io_tls_trust_count(fio_io_tls_s *tls)
```
Returns the number of `fio_io_tls_trust_add` instructions.
This could be used when deciding if to disable peer verification or not.
If `fio_io_tls_trust_add` was never called, zero (0) is returned.
_Symbol type:_ `function`
#### `fio_io_tls_each`
```c
int fio_io_tls_each(fio_io_tls_each_s)
```
Calls callbacks for certificate, trust certificate and ALPN added.
_Symbol type:_ `function`
#### `fio_io_tls_each`
```c
#define fio_io_tls_each(tls_, ...) \
fio_io_tls_each(((fio_io_tls_each_s){.tls = tls_, __VA_ARGS__}))
```
`fio_io_tls_each` helper macro, see `fio_io_tls_each_s` for named
arguments.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_tls_default_functions`
```c
fio_io_functions_s fio_io_tls_default_functions(fio_io_functions_s *)
```
If `NULL` returns current default, otherwise sets it. Set before start.
_Symbol type:_ `function`
#### `fio_io_async_queue`
```c
inline fio_queue_s *fio_io_async_queue(fio_io_async_s *q)
```
Returns the current task queue associated with the IO Async Queue.
_Symbol type:_ `function`
#### `fio_io_async_attach`
```c
void fio_io_async_attach(fio_io_async_s *q, uint32_t threads)
```
Attaches an IO Async Queue for use in multi-threaded (non IO) tasks.
This function can be called multiple times for the same (or other) queue, as
long as the async queue (`fio_io_async_s`) was previously initialized using
`FIO_IO_ASYN_INIT` or zeroed out. i.e.:
static fio_io_async_s SLOW_HTTP_TASKS = FIO_IO_ASYN_INIT;
fio_io_async_attach(&SLOW_HTTP_TASKS, 32);
_Symbol type:_ `function`
#### `fio_io_async`
```c
#define fio_io_async(q_, ...) fio_queue_push((q_)->q, __VA_ARGS__)
```
Pushes a task to an IO Async Queue (macro helper).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_io_async_every`
```c
void fio_io_async_every(fio_io_async_s *q, fio_timer_schedule_args_s)
```
Schedules a timer bound task for the async queue (`fio_timer_schedule`).
_Symbol type:_ `function`
#### `fio_io_async_every`
```c
#define fio_io_async_every(async, ...) \
fio_io_async_every(async, (fio_timer_schedule_args_s){__VA_ARGS__})
```
Schedules a timer bound task, for the async queue, see `fio_timer_schedule`.
Possible "named arguments" (fio_timer_schedule_args_s members) include:
* The timer function. If it returns a non-zero value, the timer stops:
int (*fn)(void *, void *)
* Opaque user data:
void *udata1
* Opaque user data:
void *udata2
* Called when the timer is done (finished):
void (*on_stop)(void *, void *)
* Timer interval, in milliseconds:
uint32_t every
* The number of times the timer should be performed. -1 == infinity:
int32_t repetitions
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
-----------------------------------------------------
# `./fio-stl/404 ipc.h`
41 public symbols.
### Macros
#### `FIO_IPC_URL_MAX_LENGTH`
```c
#define FIO_IPC_URL_MAX_LENGTH 1024
```
_Symbol type:_ `macro`
#### `FIO_IPC_MAX_LENGTH`
```c
#define FIO_IPC_MAX_LENGTH (128ULL * 1024U * 1024U)
```
_Symbol type:_ `macro`
#### `FIO_IPC_DATA`
```c
#define FIO_IPC_DATA(...) \
(fio_buf_info_s[]) { \
__VA_ARGS__, { .len = ((size_t)-1) } \
}
```
A helper macro for composing multiple buffers into the request. Fill with
fio_buf_info_s - will stop when a buffer has zero length. Use:
.data = FIO_IPC_DATA(FIO_BUF_INFO1((char *)"example:"),
FIO_BUF_INFO2(&number, sizeof(number)))
_Symbol type:_ `macro`
#### `FIO_IPC_EXCLUDE_SELF`
```c
#define FIO_IPC_EXCLUDE_SELF ((fio_io_s *)((char *)-1LL))
```
Excludes current process from fio_ipc_local (to be set in ipc->from)
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_ENCRYPTED`
```c
#define FIO_IPC_FLAG_ENCRYPTED ((uint16_t)1UL << 0)
```
Set if message is in its encrypted state - do NOT edit manually
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_DONE`
```c
#define FIO_IPC_FLAG_DONE ((uint16_t)1UL << 1)
```
If set, calls `on_done` rather than `call` - requires FIO_IPC_FLAG_REPLY
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_OPCODE`
```c
#define FIO_IPC_FLAG_OPCODE ((uint16_t)1UL << 2)
```
Set if message callbacks are mapped using op-codes
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_WORKERS`
```c
#define FIO_IPC_FLAG_WORKERS ((uint16_t)1UL << 3)
```
If set, delivered to worker processes as well as master
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_CLUSTER`
```c
#define FIO_IPC_FLAG_CLUSTER ((uint16_t)1UL << 4)
```
If set, delivered to remote machines - requires FIO_IPC_FLAG_OPCODE
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_REPLY`
```c
#define FIO_IPC_FLAG_REPLY ((uint16_t)1UL << 5)
```
If set, calls `on_reply` rather than `call`
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_PING`
```c
#define FIO_IPC_FLAG_PING ((uint16_t)1UL << 6)
```
If set, this is an internal ping/keepalive message (not dispatched to user)
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_TEST`
```c
#define FIO_IPC_FLAG_TEST(msg, flag) (((msg)->routing_flags & flag) == flag)
```
If flag is set == 1, otherwise zero
_Symbol type:_ `macro`
#### `FIO_IPC_FLAG_IF`
```c
#define FIO_IPC_FLAG_IF(bcond, flag) (((uint16_t)0UL - (bcond)) & flag)
```
If flag is set == flag, otherwise zero
_Symbol type:_ `macro`
### Types
#### `fio_ipc_s`
```c
struct fio_ipc_s {
fio_io_s *from; /* IO to caller - set by receiver (unsent) */
/* ----- wire format starts here ----- */
uint32_t len; /* Length of data[] (AAD - authenticated, unencrypted) */
uint16_t flags; /* User settable flags (AAD - authenticated, unencrypted) */
uint16_t routing_flags; /* Internal (AAD - authenticated, unencrypted) */
uint64_t timestamp; /* timestamp (unencrypted, used for nonce) */
uint64_t id; /* 8 random bytes (unencrypted, used for nonce) */
union {
void (*call)(struct fio_ipc_s *); /* function pointer to call (encrypted) */
uint32_t opcode; /* op-code to execute*/
};
void (*on_reply)(struct fio_ipc_s *); /* run on caller (encrypted) */
void (*on_done)(struct fio_ipc_s *); /* run on caller (encrypted) */
void *udata; /* opaque, valid only in caller (encrypted) */
char data[]; /* Variable-length data + 16-byte MAC at end (encrypted) */
}
```
IPC message structure (reference counted)
_Symbol type:_ `type`
#### `fio_ipc_args_s`
```c
typedef struct {
void (*call)(fio_ipc_s *); /* function to call */
void (*on_reply)(fio_ipc_s *); /* (optional) reply callback */
void (*on_done)(fio_ipc_s *); /* (optional) reply finished callback */
fio_io_s *exclude; /* (optional) IO to exclude from delivery */
uint64_t timestamp; /* (optional) to force timestamp */
uint64_t id; /* (optional) to force an id value */
uint32_t opcode; /* replaces `call` with op-code if non-zero */
uint16_t flags; /* (optional) user-opaque flags */
bool cluster; /* if set, this is intended for all machines in cluster */
bool workers; /* if set, this is intended for master + workers */
void *udata; /* opaque pointer data for reply */
fio_buf_info_s *data; /* payload (see FIO_IPC_DATA) */
} fio_ipc_args_s
```
IPC call arguments
_Symbol type:_ `type`
#### `fio_ipc_opcode_s`
```c
struct fio_ipc_opcode_s {
uint32_t opcode; /* Unique op-code value */
void (*call)(struct fio_ipc_s *); /* function to call */
void (*on_reply)(struct fio_ipc_s *); /* (optional) reply callback */
void (*on_done)(struct fio_ipc_s *); /* (optional) reply finished callback */
void *udata; /* opaque, valid only in caller (encrypted) */
}
```
_Symbol type:_ `type`
#### `fio_ipc_reply_args_s`
```c
typedef struct {
fio_ipc_s *ipc;
fio_buf_info_s *data;
uint64_t timestamp; /* (optional) override timestamp, 0 = use current time */
uint64_t id; /* (optional) override id, 0 = use original request id */
uint16_t flags; /* (optional) override flags, 0 = use original request */
uint8_t done;
uint8_t flags_set; /* set to 1 if flags should be used (allows flags=0) */
} fio_ipc_reply_args_s
```
IPC reply arguments
_Symbol type:_ `type`
### Functions
#### `fio_ipc_url`
```c
const char *fio_ipc_url(void)
```
Returns the IPC url to listen to (for incoming connections).
_Symbol type:_ `function`
#### `fio_ipc_url_set`
```c
int fio_ipc_url_set(const char *url)
```
Sets the IPC url to listen to (for incoming connections).
Can only be called on the master process and only before the IO reactor.
_Symbol type:_ `function`
#### `fio_ipc_opcode_register`
```c
int fio_ipc_opcode_register(fio_ipc_opcode_s opcode)
```
Registers an op-code for message routing.
There are two types of messages:
1. Fast Path - function pointers in the message payload (local IPC only).
2. Safe Path - Op-Code in the `call` payload (multi-machine RCP).
Op-Codes MUST be non-zero uint32_t values.
RESERVED: op-codes >= 0xFF000000 are reserved for internal use.
Note: Thread safety requires that this be called before fio_io_start.
Returns -1 or failure (not on master process / already registered)
_Symbol type:_ `function`
#### `fio_ipc_opcode_register`
```c
#define fio_ipc_opcode_register(...) \
fio_ipc_opcode_register((fio_ipc_opcode_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_opcode`
```c
const fio_ipc_opcode_s *fio_ipc_opcode(uint32_t opcode)
```
Returns a pointer to a registered op-code, or NULL if missing.
_Symbol type:_ `function`
#### `fio_ipc_call`
```c
#define fio_ipc_call(...) fio_ipc_send(fio_ipc_new(__VA_ARGS__))
```
Call arbitrary code in master process (worker → master / master → master).
The `call` function pointer is executed on the master process.
Replies are sent back to the caller via `on_reply` callback.
When all replies are done, `on_done` is called.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_local`
```c
#define fio_ipc_local(...) fio_ipc_send(fio_ipc_new(.workers = 1, __VA_ARGS__))
```
Call arbitrary code in master process and workers (all local).
Replies can be sent back to the caller only from its local master process.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_cluster`
```c
#define fio_ipc_cluster(...) \
fio_ipc_send(fio_ipc_new(.cluster = 1, __VA_ARGS__))
```
Call arbitrary code in master process of every machine in cluster.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_broadcast`
```c
#define fio_ipc_broadcast(...) \
fio_ipc_send(fio_ipc_new(.workers = 1, .cluster = 1, __VA_ARGS__))
```
Call arbitrary code in master process of every machine in cluster.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_reply`
```c
void fio_ipc_reply(fio_ipc_reply_args_s args)
```
Send a response to the caller process (master → caller).
Can be called multiple times for streaming responses.
Set `done = 1` on the last reply.
_Symbol type:_ `function`
#### `fio_ipc_reply`
```c
#define fio_ipc_reply(r, ...) \
fio_ipc_reply((fio_ipc_reply_args_s){.ipc = (r), __VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_new`
```c
fio_ipc_s *fio_ipc_new(fio_ipc_args_s args)
```
Authors a message without sending it.
Used internally but available for "faking" IPC or when composing a unified
code path for local execution.
_Symbol type:_ `function`
#### `fio_ipc_new`
```c
#define fio_ipc_new(...) fio_ipc_new((fio_ipc_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_ipc_dup`
```c
fio_ipc_s *fio_ipc_dup(fio_ipc_s *msg)
```
Duplicate a message (increment reference count).
Use when storing messages for later processing.
Every dup() must be matched with a free().
_Symbol type:_ `function`
#### `fio_ipc_free`
```c
void fio_ipc_free(fio_ipc_s *msg)
```
Free a message (decrement reference count).
Message is destroyed when reference count reaches zero.
_Symbol type:_ `function`
#### `fio_ipc_detach`
```c
void fio_ipc_detach(fio_ipc_s *msg)
```
Detaches the IPC message from it's originating IO.
Call if storing or performing non-IPC actions using the IPC message.
_Symbol type:_ `function`
#### `fio_ipc_send`
```c
void fio_ipc_send(fio_ipc_s *ipc)
```
Encrypts the IPC message(!), sends it for execution and frees it.
Message will be sent according to the flags set (see fio_ipc_new):
- Only to Master (fio_ipc_call);
- To Master and Workers (fio_ipc_local);
- To Master on Every Machine (fio_ipc_cluster);
- To Master and Workers on Every Machine (fio_ipc_broadcast);
Note: Takes ownership of the message's memory.
Note: overwrites after_send unless `ipc->from == FIO_IPC_EXCLUDE_SELF`
Note: excludes ipc->from.
Note: The message is encrypted and unusable once call returns - pass it the
last reference.
_Symbol type:_ `function`
#### `fio_ipc_send_to`
```c
void fio_ipc_send_to(fio_io_s *to, fio_ipc_s *ipc)
```
Encrypts the IPC message(!) and sends it to target IO - frees the message.
Note: Takes ownership of the message's memory.
Note: The message is encrypted and unusable until the message was sent and
either freed or the `after_send` callback was called.
_Symbol type:_ `function`
#### `fio_ipc_after_send`
```c
void fio_ipc_after_send(fio_ipc_s *ipc, void (*fn)(fio_ipc_s *, void *), void *udata)
```
_Symbol type:_ `function`
#### `fio_ipc_encrypt`
```c
void fio_ipc_encrypt(fio_ipc_s *m)
```
Encrypt IPC message before sending them anywhere.
_Symbol type:_ `function`
#### `fio_ipc_decrypt`
```c
inline int fio_ipc_decrypt(fio_ipc_s *m)
```
Decrypt IPC message when received.
_Symbol type:_ `function`
#### `fio_ipc_cluster_listen`
```c
fio_io_listener_s *fio_ipc_cluster_listen(uint16_t port)
```
Listens to cluster connections on the port listed, auto-connects to peers.
This does NOT improve message exchange or pub/sub performance. This is
designed for downtime mitigation (rotating pods) / data tunneling and client
load balancing (without message load balancing).
All server instances get all `cluster` messages (e.g., pub/sub cluster).
Note: uses the environment's (shared) secret for rudimentary encryption
without forward secrecy. Rotate secrets when possible (requires restart).
Good for trusted data centers, Kubernetes pods, etc'.
_Symbol type:_ `function`
#### `fio_ipc_cluster_connect`
```c
void fio_ipc_cluster_connect(const char *url)
```
Manually connects to cluster peers. Usually unnecessary.
_Symbol type:_ `function`
#### `fio_ipc_cluster_port`
```c
uint16_t fio_ipc_cluster_port(void)
```
Returns the last port number passed to either fio_ipc_cluster_listen or
fio_ipc_cluster_connect - zero if none.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/405 tls13.h`
1 public symbols.
### Functions
#### `fio_tls13_io_functions`
```c
fio_io_functions_s fio_tls13_io_functions(void)
```
Returns the TLS 1.3 IO functions.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/420 pubsub.h`
26 public symbols.
### Macros
#### `FIO_PUBSUB_FUTURE_LIMIT_MS`
```c
#define FIO_PUBSUB_FUTURE_LIMIT_MS 60000ULL
```
Maximum time in milliseconds to allow "future" messages to be delivered.
If the module detects someone publishing a future message, it will refuse to
deliver the message to subscribers.
Instead, the module will notify the history engines, allowing them to buffer
future messages for future delivery.
_Symbol type:_ `macro`
#### `FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT`
```c
#define FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT (1ULL << 28)
```
The default cache limit - 256Mb
_Symbol type:_ `macro`
### Types
#### `fio_pubsub_subscribe_args_s`
```c
typedef struct {
/**
* The subscription owner - if none, the subscription is owned by the system.
*
* Note:
*
* Both the system and the `io` objects each manage channel listing
* which allows only a single subscription to the same channel.
*
* This means a single subscription per channel per IO and a single
* subscription per channel for the global system unless managing the
* subscription handle manually.
*/
fio_io_s *io;
/**
* A named `channel` to which the message was sent.
*
* Subscriptions require a match by both channel name and namespace filter.
*/
fio_buf_info_s channel;
/**
* The callback to be called for each message forwarded to the subscription.
*/
void (*on_message)(fio_pubsub_msg_s *msg);
/** An optional callback for when a subscription is canceled. */
void (*on_unsubscribe)(void *udata);
/** The opaque udata value is ignored and made available to the callbacks. */
void *udata;
/** The queue to which the callbacks should be routed. May be NULL. */
fio_queue_s *queue;
/**
* OPTIONAL: subscription handle return value - should be NULL when using
* automatic memory management with the IO or global environment.
*
* When set, the `io` pointer will be ignored and the subscription object
* handle will be written to the `subscription_handle_ptr` which MUST be
* used when unsubscribing.
*
* NOTE: this could cause subscriptions and memory leaks unless properly
* handled.
*/
uintptr_t *subscription_handle_ptr;
/** Replay cached messages (if any) since supplied time in milliseconds. */
uint64_t replay_since;
/** A numerical namespace `filter` subscribers need to match. */
int16_t filter;
/** If set, pattern matching will be used (name is a pattern). */
uint8_t is_pattern;
/** If set, subscription will be limited to the root / master process. */
uint8_t master_only;
} fio_pubsub_subscribe_args_s
```
_Symbol type:_ `type`
#### `fio_pubsub_publish_args_s`
```c
typedef struct {
struct fio_pubsub_engine_s const *engine;
fio_io_s *from;
uint64_t id;
uint64_t timestamp;
fio_buf_info_s channel;
fio_buf_info_s message;
int16_t filter;
} fio_pubsub_publish_args_s
```
Publish arguments
_Symbol type:_ `type`
#### `fio_pubsub_engine_s`
```c
struct fio_pubsub_engine_s {
/** Called when engine is detached */
void (*detached)(const struct fio_pubsub_engine_s *eng);
/** Called when a subscription is created */
void (*subscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a pattern subscription is created */
void (*psubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a subscription is removed */
void (*unsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a pattern subscription is removed */
void (*punsubscribe)(const struct fio_pubsub_engine_s *eng,
const fio_buf_info_s channel,
int16_t filter);
/** Called when a message is published */
void (*publish)(const struct fio_pubsub_engine_s *eng,
const fio_pubsub_msg_s *msg);
}
```
Engine structure for external pub/sub backends.
EXECUTION CONTEXT:
- Publish callback can be called from any thread / process.
- Subscription callbacks are called from the MASTER process only
- Subscription callbacks are called from the main event loop thread
- Callbacks MUST NOT block (defer long operations)
_Symbol type:_ `type`
#### `fio_pubsub_history_s`
```c
struct fio_pubsub_history_s {
/** Cleanup callback - called when history manager is detached */
void (*detached)(const struct fio_pubsub_history_s *hist);
/**
* Stores a message in history.
*
* Returns 0 on success, -1 on error.
* Called when a message is published (master only).
*/
int (*push)(const struct fio_pubsub_history_s *hist, fio_pubsub_msg_s *msg);
/**
* Replay messages since timestamp by calling callback for each.
*
* Returns 0 if replay was handled, -1 if this manager cannot replay.
*
* MUST call the `on_done` callback to handle possible cleanup.
*/
int (*replay)(const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter,
uint64_t since,
void (*on_message)(fio_pubsub_msg_s *msg, void *udata),
void (*on_done)(void *udata),
void *udata);
/**
* Get oldest available timestamp for a channel.
*
* Returns UINT64_MAX if no history available.
*/
uint64_t (*oldest)(const struct fio_pubsub_history_s *hist,
fio_buf_info_s channel,
int16_t filter);
}
```
History storage interface - completely separate from engines.
EXECUTION CONTEXT:
- All callbacks are called from the MASTER process only
- Callbacks are called from the main event loop thread
- Callbacks MUST NOT block (defer long operations)
- Callbacks SHOULD NOT be called directly by the user
_Symbol type:_ `type`
### Functions
#### `fio_pubsub_subscribe`
```c
void fio_pubsub_subscribe(fio_pubsub_subscribe_args_s args)
```
Subscribe to a channel.
In worker processes, this uses IPC to notify the master.
_Symbol type:_ `function`
#### `fio_pubsub_subscribe`
```c
#define fio_pubsub_subscribe(...) \
fio_pubsub_subscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_unsubscribe`
```c
int fio_pubsub_unsubscribe(fio_pubsub_subscribe_args_s args)
```
Unsubscribe from a channel.
Returns 0 on success, -1 if subscription not found.
_Symbol type:_ `function`
#### `fio_pubsub_unsubscribe`
```c
#define fio_pubsub_unsubscribe(...) \
fio_pubsub_unsubscribe((fio_pubsub_subscribe_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_publish`
```c
void fio_pubsub_publish(fio_pubsub_publish_args_s args)
```
Publish a message to a channel.
In worker processes, this uses IPC to notify the master.
_Symbol type:_ `function`
#### `fio_pubsub_publish`
```c
#define fio_pubsub_publish(...) \
fio_pubsub_publish((fio_pubsub_publish_args_s){__VA_ARGS__})
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_pubsub_defer`
```c
void fio_pubsub_defer(fio_pubsub_msg_s *msg)
```
Pushes execution of the on_message callback to the end of the queue.
_Symbol type:_ `function`
#### `fio_pubsub_match_fn_set`
```c
void fio_pubsub_match_fn_set(uint8_t (*match_cb)(fio_str_info_s, fio_str_info_s))
```
_Symbol type:_ `function`
#### `fio_pubsub_msg2ipc`
```c
fio_ipc_s *fio_pubsub_msg2ipc(fio_pubsub_msg_s *msg)
```
Returns the underlying IPC message buffer carrying the message data.
This allows message deferral (use fio_ipc_dup) and tighter control over the
message's lifetime.
_Symbol type:_ `function`
#### `fio_pubsub_ipc2msg`
```c
fio_pubsub_msg_s fio_pubsub_ipc2msg(fio_ipc_s *ipc)
```
Extract pub/sub message from IPC message
_Symbol type:_ `function`
#### `fio_pubsub_engine_attach`
```c
void fio_pubsub_engine_attach(fio_pubsub_engine_s *engine)
```
Attach an engine to the pub/sub system - moves ownership to the system.
_Symbol type:_ `function`
#### `fio_pubsub_engine_detach`
```c
void fio_pubsub_engine_detach(fio_pubsub_engine_s *engine)
```
Detach an engine from the pub/sub system - frees system's reference.
_Symbol type:_ `function`
#### `fio_pubsub_engine_ipc`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_ipc(void)
```
Returns the builtin engine for publishing to the process group (IPC).
_Symbol type:_ `function`
#### `fio_pubsub_engine_cluster`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_cluster(void)
```
Returns the builtin engine for multi-machine cluster publishing (RPC).
_Symbol type:_ `function`
#### `fio_pubsub_engine_default`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default(void)
```
Returns the current default engine associated with the pub/sub system.
_Symbol type:_ `function`
#### `fio_pubsub_engine_default_set`
```c
fio_pubsub_engine_s const *fio_pubsub_engine_default_set( fio_pubsub_engine_s const *engine)
```
Sets the current default engine associated with the pub/sub system.
_Symbol type:_ `function`
#### `fio_pubsub_history_attach`
```c
int fio_pubsub_history_attach(const fio_pubsub_history_s *manager, uint8_t priority)
```
Attach a history manager with the given priority.
Multiple history managers can be attached. All managers receive push()
calls when messages are published. For replay(), managers are tried in
priority order (highest first) until one can handle the request.
_Symbol type:_ `function`
#### `fio_pubsub_history_detach`
```c
void fio_pubsub_history_detach(const fio_pubsub_history_s *manager)
```
Detach a history manager.
_Symbol type:_ `function`
#### `fio_pubsub_history_push_all`
```c
void fio_pubsub_history_push_all(fio_pubsub_msg_s *msg)
```
Pushes a pub/sub message to all history containers.
Use ONLY by an engine, if the message needs to be saved to the history
managers in the process but is never delivered locally.
_Symbol type:_ `function`
#### `fio_pubsub_history_cache`
```c
fio_pubsub_history_s const *fio_pubsub_history_cache(size_t size_limit)
```
Get the built-in in-memory history manager and set it's byte-size limit.
A zero value size_limit will be replaced with the following default:
- Environment value of WEBSITE_MEMORY_LIMIT_MB * 1024 * 1024
- Environment value of WEBSITE_MEMORY_LIMIT_KB
- Environment value of WEBSITE_MEMORY_LIMIT
- FIO_PUBSUB_HISTORY_DEFAULT_CACHE_SIZE_LIMIT
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/422 redis.h`
10 public symbols.
### Macros
#### `FIO_REDIS_READ_BUFFER`
```c
#define FIO_REDIS_READ_BUFFER 65536
```
Size of the read buffer for the Redis connection.
NOTE: must fit fio_redis_connection_s.buf_pos (uint32_t).
_Symbol type:_ `macro`
#### `FIO_REDIS_MAX_BATCH`
```c
#define FIO_REDIS_MAX_BATCH 128
```
Maximum number of complete messages processed per `on_data` event.
When the cap is reached with more data buffered, processing continues in a
deferred task. This keeps any single event-loop callback small - downstream
work per message is limited to scheduling (defer / publish), never I/O.
_Symbol type:_ `macro`
### Types
#### `fio_redis_args_s`
```c
typedef struct {
/**
* Redis server URL.
*
* Supported formats:
* - "redis://host:port"
* - "redis://host" (default port 6379)
* - "host:port" (no scheme)
* - "host" (no scheme, default port 6379)
* - NULL or empty → defaults to "localhost:6379"
*/
const char *url;
/** Redis server's password, if any (folded into the HELLO 3 handshake) */
const char *auth;
/** Length of auth string (0 = auto-detect with strlen) */
size_t auth_len;
/** Ping interval in seconds (0 = default 300 seconds) */
uint8_t ping_interval;
/**
* Cumulative payload budget per top-level Redis message, in bytes.
*
* Budget = Σ(all string payload bytes) + 32 × (count of ALL objects -
* String, Array, Map, Bool, Number, etc.). Checked BEFORE allocating or
* appending; a breach logs an error and disconnects.
*
* 0 = default (16MB). Protects against hostile / corrupt servers declaring
* huge `$` allocations or oversized replies.
*/
size_t payload_limit;
} fio_redis_args_s
```
Arguments for creating a Redis engine
_Symbol type:_ `type`
#### `fio_redis_state_e`
```c
typedef enum {
/** The engine stopped after a failed HELLO handshake (or NULL was queried). */
FIO_REDIS_STATE_ERROR,
/** No socket is attached, or the RESP3 HELLO handshake is still pending. */
FIO_REDIS_STATE_CONNECTING,
/** A socket is attached and its RESP3 HELLO handshake completed. */
FIO_REDIS_STATE_CONNECTED,
} fio_redis_state_e
```
Redis connection state, observed from the IO thread.
_Symbol type:_ `type`
### Functions
#### `fio_redis_new`
```c
fio_pubsub_engine_s *fio_redis_new(fio_redis_args_s args)
```
Creates a Redis pub/sub engine with reference count = 1.
The engine is active only after the IO reactor starts running.
The caller owns the returned reference and must call fio_redis_free()
when done. Attaching to pub/sub does NOT transfer ownership.
Returns a pointer to the engine or NULL on error.
_Symbol type:_ `function`
#### `fio_redis_new`
```c
#define fio_redis_new(...) fio_redis_new((fio_redis_args_s){__VA_ARGS__})
```
Creates a Redis pub/sub engine (named arguments helper macro).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_redis_dup`
```c
fio_pubsub_engine_s *fio_redis_dup(fio_pubsub_engine_s *engine)
```
Increments the reference count and returns the engine.
Use this when you need to share the engine across multiple owners.
Each call to fio_redis_dup() must be balanced with fio_redis_free().
_Symbol type:_ `function`
#### `fio_redis_free`
```c
void fio_redis_free(fio_pubsub_engine_s *engine)
```
Releases the caller's reference to the engine.
This function simply decrements the reference count. If the ref reaches 0
(no other refs held), fio___redis_destroy() fires immediately:
sets running=0, closes connections, drains the command queue, frees memory.
The engine stays alive as long as any other ref is held (e.g. the pub/sub
system's ref taken via subscribe/psubscribe). The pub/sub system releases
its ref via the on_detached callback when fio_pubsub_engine_detach() fires.
The caller does NOT need to call fio_pubsub_engine_detach() before freeing.
Calling fio_pubsub_engine_detach() before fio_redis_free() is also safe
(detach releases the system ref; free releases the caller ref).
Safe to call with NULL (no-op).
_Symbol type:_ `function`
#### `fio_redis_state`
```c
fio_redis_state_e fio_redis_state(fio_pubsub_engine_s const *engine)
```
Returns the current Redis connection state.
This function must be called from the IO thread. Commands can be sent in
every state: they queue until the RESP3 HELLO handshake completes.
_Symbol type:_ `function`
#### `fio_redis_send`
```c
int fio_redis_send(fio_pubsub_engine_s *engine, FIOBJ command, void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply, void *udata), void *udata)
```
Sends a Redis command through the engine's connection.
The response will be sent back using the optional callback. `udata` is passed
along untouched.
The `command` should be a FIOBJ array containing the command and arguments.
Note: NEVER call Pub/Sub commands (SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE,
PUNSUBSCRIBE) using this function, as it will violate the Redis connection's
protocol.
Returns 0 on success, -1 on error.
_Symbol type:_ `function`
-----------------------------------------------------
# `./fio-stl/430 http api.h`
153 public symbols.
### Macros
#### `FIO_HTTP_DEFAULT_MAX_HEADER_SIZE`
```c
#define FIO_HTTP_DEFAULT_MAX_HEADER_SIZE 32768 /* (1UL << 15) */
```
The default HTTP total header size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_MAX_LINE_LEN`
```c
#define FIO_HTTP_DEFAULT_MAX_LINE_LEN 8192 /* (1UL << 13) */
```
The default HTTP header line limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_MAX_BODY_SIZE`
```c
#define FIO_HTTP_DEFAULT_MAX_BODY_SIZE 33554432 /* (1UL << 25) */
```
The default HTTP payload size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE`
```c
#define FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE 262144 /* (1UL << 18) */
```
The default WebSocket message size limit in bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_TIMEOUT`
```c
#define FIO_HTTP_DEFAULT_TIMEOUT 50
```
The default timeout for HTTP connections.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_TIMEOUT_LONG`
```c
#define FIO_HTTP_DEFAULT_TIMEOUT_LONG 50
```
The default timeout for long held HTTP connections (WebSockets / SSE).
_Symbol type:_ `macro`
#### `FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER`
```c
#define FIO_HTTP_SHOW_CONTENT_LENGTH_HEADER 0
```
Adds a "content-length" header to the HTTP handle (usually redundant).
_Symbol type:_ `macro`
#### `FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT`
```c
#define FIO_HTTP_WEBSOCKET_WRITE_VALIDITY_TEST_LIMIT ((1UL << 16) - 10UL)
```
UTF-8 validity tests will be performed only for data shorter than this.
_Symbol type:_ `macro`
#### `FIO_WEBSOCKET_STATS`
```c
#define FIO_WEBSOCKET_STATS 0
```
If true, logs longest WebSocket round-trips (using FIO_LOG_INFO).
_Symbol type:_ `macro`
#### `FIO_HTTP_WEBSOCKET_DEFLATE_MIN`
```c
#define FIO_HTTP_WEBSOCKET_DEFLATE_MIN 1024
```
Messages smaller than this are not compressed (fits in a single TCP/IP
packet, compression saves no network overhead).
_Symbol type:_ `macro`
#### `FIO_HTTP_EXACT_LOGGING`
```c
#define FIO_HTTP_EXACT_LOGGING 0
```
By default, facil.io logs the HTTP request cycle using a fuzzy starting and
ending point for the time stamp.
The fuzzy timestamp includes delays that aren't related to the HTTP request
and may ignore time passed due to timestamp caching.
On the other hand, `FIO_HTTP_EXACT_LOGGING` collects exact time stamps to
measure the time it took to process the HTTP request (excluding time spent
reading / writing the data from the network).
Due to the preference to err on the side of higher performance, fuzzy
time-stamping is the default.
_Symbol type:_ `macro`
#### `FIO_HTTP_BODY_RAM_LIMIT`
```c
#define FIO_HTTP_BODY_RAM_LIMIT (1 << 17)
```
The HTTP handle automatically switches between RAM storage and file storage
once the HTTP body (payload) reaches a certain size. This control this point
of transition
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_LIMIT`
```c
#define FIO_HTTP_CACHE_LIMIT 0 /* ((1UL << 6) + (1UL << 5)) */
```
Each of the HTTP String Caches will be limited to this String count.
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_STR_MAX_LEN`
```c
#define FIO_HTTP_CACHE_STR_MAX_LEN (1 << 12)
```
The HTTP handle will avoid caching strings longer than this value.
_Symbol type:_ `macro`
#### `FIO_HTTP_CACHE_USES_MUTEX`
```c
#define FIO_HTTP_CACHE_USES_MUTEX 1
```
The HTTP cache will use a mutex to allow headers to be set concurrently.
_Symbol type:_ `macro`
#### `FIO_HTTP_PRE_CACHE_KNOWN_HEADERS`
```c
#define FIO_HTTP_PRE_CACHE_KNOWN_HEADERS 1
```
Adds a static cache for common HTTP header names.
_Symbol type:_ `macro`
#### `FIO_HTTP_DEFAULT_INDEX_FILENAME`
```c
#define FIO_HTTP_DEFAULT_INDEX_FILENAME "index"
```
The default file name when a static file response points to a folder.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATIC_FILE_COMPLETION`
```c
#define FIO_HTTP_STATIC_FILE_COMPLETION 1
```
Attempts to auto-complete static file paths with missing extensions.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT`
```c
#define FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT (1UL << 21) /* 2 MiB */
```
Maximum file size (in bytes) for on-disk static file compression.
_Symbol type:_ `macro`
#### `FIO_HTTP_LOG_X_REQUEST_START`
```c
#define FIO_HTTP_LOG_X_REQUEST_START 1
```
_Symbol type:_ `macro`
#### `FIO_HTTP_ENFORCE_LOWERCASE_HEADERS`
```c
#define FIO_HTTP_ENFORCE_LOWERCASE_HEADERS 0
```
If true, the HTTP handle will copy input header names to lower case.
_Symbol type:_ `macro`
#### `FIO_HTTP_PATH_EACH`
```c
#define FIO_HTTP_PATH_EACH(path, pos)
```
Loops over each section of the path, decrypting percent encoding as
necessary.
The macro accepts the following:
- `path`: the path string - accessible using fio_http_path(h).
- `pos` : the name of the variable to use for accessing the section.
The variable `pos` is a `fio_buf_info_s`.
**Note**: the macro will break if a path's section length is greater than
(about) 4063 bytes.
_Symbol type:_ `macro`
#### `FIO_HTTP_HEADER_EACH_VALUE`
```c
#define FIO_HTTP_HEADER_EACH_VALUE(http_handle, is_request, header_name, pos) \
for (fio_http_header_each_s pos = fio_http_header_each_first((http_handle), \
(is_request), \
(header_name)); \
pos.value.buf; \
fio_http_header_each_next(&pos, (http_handle)))
```
Iterate repeated request (`is_request = 1`) or response (`0`) header
fields and their comma-separated values; `pos` is a chosen cursor name.
SP / HTAB around each value is trimmed. Use `.value.buf` and `.value.len`,
not string functions, for the non-NUL-terminated slice. Do not mutate the
handle while iterating. `break` / `continue` work as in a regular for loop.
Example:
FIO_HTTP_HEADER_EACH_VALUE(h, 1, FIO_STR_INFO1("accept"), item) {
FIO_HTTP_HEADER_EACH_PROPERTY(item, property) {
// property.name and property.value are length-delimited slices.
}
}
_Symbol type:_ `macro`
#### `FIO_HTTP_HEADER_EACH_PROPERTY`
```c
#define FIO_HTTP_HEADER_EACH_PROPERTY(value_, pos) \
for (fio_http_header_each_s pos = \
fio_http_header_each_property_first(&(value_).properties); \
pos.value.buf; \
fio_http_header_each_property_next(&pos))
```
Iterate semicolon-separated properties for a value cursor; `pos` is a
chosen name. Names and values are trimmed of surrounding SP / HTAB.
A missing `=` or an empty right-hand side yields `value.len == 0` with a
non-NULL `value.buf`; stop iteration only when `value.buf == NULL`.
Quoted strings remain quoted, without decoding. `break` / `continue` work.
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_STREAMING`
```c
#define FIO_HTTP_STATE_STREAMING 1
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_FINISHED`
```c
#define FIO_HTTP_STATE_FINISHED 2
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_UPGRADED`
```c
#define FIO_HTTP_STATE_UPGRADED 4
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_WEBSOCKET`
```c
#define FIO_HTTP_STATE_WEBSOCKET 8
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_SSE`
```c
#define FIO_HTTP_STATE_SSE 16
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_COOKIES_PARSED`
```c
#define FIO_HTTP_STATE_COOKIES_PARSED 32
```
_Symbol type:_ `macro`
#### `FIO_HTTP_STATE_FREEING`
```c
#define FIO_HTTP_STATE_FREEING 64
```
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_DYNAMIC`
```c
#define FIO_HTTP_CFLAG_COMPRESS_DYNAMIC 1
```
Controller flags (cflags) for opt-in compression features.
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_WS`
```c
#define FIO_HTTP_CFLAG_COMPRESS_WS 2
```
_Symbol type:_ `macro`
#### `FIO_HTTP_CFLAG_COMPRESS_STATIC`
```c
#define FIO_HTTP_CFLAG_COMPRESS_STATIC 4
```
_Symbol type:_ `macro`
### Types
#### `fio_http_s`
```c
struct fio_http_s
```
The HTTP Handle type.
Note that the type is NOT designed to be thread-safe.
_Symbol type:_ `type`
#### `fio_http_controller_s`
```c
struct fio_http_controller_s
```
The HTTP Controller points to all the callbacks required by the HTTP Handler.
This allows the HTTP Handler to be somewhat protocol agnostic.
Note: if the controller callbacks aren't thread-safe, than the `http_write`
function MUST NOT be called from any thread except the thread that the
controller is expecting.
_Symbol type:_ `type`
#### `fio_http_settings_s`
```c
struct fio_http_settings_s {
/** Called before body uploads, when a client sends an `Expect` header. */
void (*pre_http_body)(fio_http_s *h);
/** Callback for HTTP requests (server) or responses (client). */
void (*on_http)(fio_http_s *h);
/** Called when a request / response cycle is finished (for WebSocket /
* SSE connections, called after `on_close`, when the connection closes). */
void (*on_finish)(fio_http_s *h);
/** Authenticate EventSource (SSE) requests, return non-zero to deny.*/
int (*on_authenticate_sse)(fio_http_s *h);
/** Authenticate WebSockets Upgrade requests, return non-zero to deny.*/
int (*on_authenticate_websocket)(fio_http_s *h);
/** Called once a WebSocket / SSE connection upgrade is complete. */
void (*on_open)(fio_http_s *h);
/** Called when a WebSocket message is received. */
void (*on_message)(fio_http_s *h, fio_buf_info_s msg, uint8_t is_text);
/** Called when an EventSource event is received. */
void (*on_eventsource)(fio_http_s *h,
fio_buf_info_s id,
fio_buf_info_s event,
fio_buf_info_s data);
/** Called when an EventSource reconnect event requests an ID. */
void (*on_eventsource_reconnect)(fio_http_s *h, fio_buf_info_s id);
/** Called for WebSocket / SSE connections when outgoing buffer is empty. */
void (*on_ready)(fio_http_s *h);
/** Called for open WebSocket / SSE connections during shutting down. */
void (*on_shutdown)(fio_http_s *h);
/** Called after a WebSocket / SSE connection is closed (for cleanup). */
void (*on_close)(fio_http_s *h);
/** (optional) the callback to be performed when the HTTP service closes. */
void (*on_stop)(struct fio_http_settings_s *settings);
/** Default opaque user data for HTTP handles (fio_http_s). */
void *udata;
/** Optional SSL/TLS support. */
fio_io_functions_s *tls_io_func;
/** Optional SSL/TLS support. */
fio_io_tls_s *tls;
/** Optional HTTP task queue (for multi-threading HTTP responses) */
fio_io_async_s *queue;
/**
* A public folder for file transfers - allows to circumvent any
* application layer logic and simply serve static files.
*
* Static file responses are attempted for `GET` and `HEAD` requests
* only (RFC 9110 §9.3 - a static file is not a valid response to other
* methods); any other method is forwarded to `on_http` (which may serve
* files explicitly by calling `fio_http_static_file_response`). On a
* miss the request is also forwarded to `on_http`. Folders resolve to
* their `index` file and missing extensions are auto-completed
* (`.html`, `.htm`, `.txt`, `.md`) - see
* `FIO_HTTP_STATIC_FILE_COMPLETION`.
*
* Pre-compressed variants are supported: when the client's
* `Accept-Encoding` allows it, an up-to-date `file.br`, `file.zstd`,
* `file.gz` or `file.zip` variant (preference order: `br`, `zstd`,
* `gzip`, `deflate`) is served instead of the original file. With
* `compress_static`, missing `.br` / `.gz` variants are also created on
* demand (written into this folder). Ranged requests are always served
* identity (no variant selection).
*
* The folder must exist when the listener starts, otherwise the setting
* is ignored (with an error log).
*/
fio_str_info_s public_folder;
/**
* The max-age value (in seconds) for caching static files sent from
* `public_folder`.
*
* Defaults to 0 (the `Cache-Control` header is not sent).
*
* Note: NOT inherited by routes - a route that serves static files must
* set its own `max_age`.
*/
size_t max_age;
/**
* The maximum total of bytes for the overall size of the request string and
* headers, combined.
*
* Defaults to FIO_HTTP_DEFAULT_MAX_HEADER_SIZE bytes.
*/
uint32_t max_header_size;
/**
* The maximum number of bytes allowed per header / request line.
*
* Defaults to FIO_HTTP_DEFAULT_MAX_LINE_LEN bytes.
*/
uint32_t max_line_len;
/**
* The maximum size of an HTTP request's body (posting / downloading).
*
* Defaults to FIO_HTTP_DEFAULT_MAX_BODY_SIZE bytes.
*/
size_t max_body_size;
/**
* The maximum WebSocket message size/buffer (in bytes) for Websocket
* connections. Defaults to FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE bytes.
*/
size_t ws_max_msg_size;
/** reserved for future use. */
intptr_t reserved1;
/** reserved for future use. */
intptr_t reserved2;
/**
* An HTTP/1.x connection timeout.
*
* Defaults to FIO_HTTP_DEFAULT_TIMEOUT seconds.
*
* Note: the connection might be closed (by other side) before timeout occurs.
*/
uint8_t timeout;
/**
* Timeout for the WebSocket connections in seconds. Defaults to
* FIO_HTTP_DEFAULT_TIMEOUT_LONG seconds.
*
* A ping will be sent whenever the timeout is reached.
*
* Connections are only closed when a ping cannot be sent (the network layer
* fails). Pongs are ignored.
*/
uint8_t ws_timeout;
/**
* Timeout for EventSource (SSE) connections in seconds. Defaults to
* FIO_HTTP_DEFAULT_TIMEOUT_LONG seconds.
*
* A ping will be sent whenever the timeout is reached.
*
* Connections are only closed when a ping cannot be sent (the network layer
* fails).
*/
uint8_t sse_timeout;
/** Timeout for client connections (only relevant in client mode). */
uint8_t connect_timeout;
/** Logging flag - set to TRUE to log HTTP requests. */
uint8_t log;
/**
* Opt-in: auto-compress static files - missing `.br` / `.gz` variants are
* created on demand and written into the `public_folder`.
*
* Creation is limited to compressible (text-like) MIME types and file
* sizes between 1024 bytes and FIO_HTTP_STATIC_FILE_COMPRESS_LIMIT (2
* MiB), and is skipped when compression wouldn't shrink the file.
* Variants whose modification time is older than the original file are
* re-created.
*
* Note: on-demand writes are attacker-triggerable - the public folder
* MUST be writable and quota'd. Pre-generating variants at deploy time is
* recommended.
*
* Note: per-route (read from the matching route's settings; routes
* inherit the listener's root value at route-creation). The value is a
* failure-memoization shift register, atomically shared by the IO
* threads - see `fio_http_static_file_response`. Detached handles (no
* settings) gate on the `FIO_HTTP_CFLAG_COMPRESS_STATIC` handle cflag
* instead, with no memoization state.
*/
uint8_t compress_static;
/**
* Opt-in: auto-compress dynamic HTTP responses on-the-fly.
*
* Applies to finished (non-streaming) single-buffer responses larger than
* 1024 bytes with a compressible (text-like) `Content-Type`, preferring
* `br` over `gzip` per the client's `Accept-Encoding`. Responses with a
* pre-set `Content-Encoding` are never re-compressed. `Vary:
* accept-encoding` is set whenever compression was considered.
*
* Note: connection-global (read from the listener's root settings when
* the connection's handle is attached) - per-route values have no effect.
*/
uint8_t compress_dynamic;
/**
* Opt-in: enable permessage-deflate for WebSocket connections.
*
* Negotiation always forces both `*_no_context_takeover` flags (RFC 7692
* allows either endpoint to request them unilaterally), keeping
* persistent per-connection compression state at ~0.
*
* Note: connection-global (read from the listener's root settings when
* the connection's handle is attached) - per-route values have no effect.
*/
uint8_t compress_ws;
}
```
_Symbol type:_ `type`
#### `fio_http_listener_s`
```c
struct fio_http_listener_s
```
_Symbol type:_ `type`
#### `fio_http_resource_action_e`
```c
typedef enum {
FIO_HTTP_RESOURCE_NONE,
FIO_HTTP_RESOURCE_INDEX,
FIO_HTTP_RESOURCE_SHOW,
FIO_HTTP_RESOURCE_NEW,
FIO_HTTP_RESOURCE_EDIT,
FIO_HTTP_RESOURCE_CREATE,
FIO_HTTP_RESOURCE_UPDATE,
FIO_HTTP_RESOURCE_DELETE,
FIO_HTTP_RESOURCE_QUERY,
} fio_http_resource_action_e
```
_Symbol type:_ `type`
#### `fio_http_cookie_same_site_e`
```c
enum fio_http_cookie_same_site_e {
/** allow the browser to dictate this property */
FIO_HTTP_COOKIE_SAME_SITE_BROWSER_DEFAULT = 0,
/** The browser sends the cookie with cross-site and same-site requests. */
FIO_HTTP_COOKIE_SAME_SITE_NONE,
/**
* The cookie is withheld on cross-site sub-requests.
*
* The cookie is sent when a user navigates to the URL from an external
* site.
*/
FIO_HTTP_COOKIE_SAME_SITE_LAX,
/** The browser sends the cookie only for same-site requests. */
FIO_HTTP_COOKIE_SAME_SITE_STRICT,
}
```
Possible values for the `same_site` property in the cookie settings.
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
_Symbol type:_ `type`
#### `fio_http_cookie_args_s`
```c
struct fio_http_cookie_args_s {
/** The cookie's name. */
fio_str_info_s name;
/** The cookie's value (leave blank to delete cookie). */
fio_str_info_s value;
/** The cookie's domain (optional). */
fio_str_info_s domain;
/** The cookie's path (optional). */
fio_str_info_s path;
/** Max Age (how long should the cookie persist), in seconds (0 == session).*/
int max_age;
/** SameSite value. */
fio_http_cookie_same_site_e same_site;
/** Limit cookie to secure connections.*/
unsigned secure : 1;
/** Limit cookie to HTTP (intended to prevent JavaScript access/hijacking).*/
unsigned http_only : 1;
/**
* Set the Partitioned (third party) cookie flag:
* https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies
*/
unsigned partitioned : 1;
}
```
This is a helper for setting cookie data.
This struct is used together with the `fio_http_cookie_set` macro. i.e.:
fio_http_set_cookie(h,
.name = FIO_STR_INFO1("my_cookie"),
.value = FIO_STR_INFO1("data"));
_Symbol type:_ `type`
#### `fio_http_write_args_s`
```c
struct fio_http_write_args_s {
/** The data to be written. */
const void *buf;
/** The length of the data to be written. */
size_t len;
/** The offset at which writing should begin. */
size_t offset;
/** If streaming a file, set this value. The file is always closed. */
int fd;
/** If the data is a buffer, this callback may be set to free it once sent. */
void (*dealloc)(void *);
/** If the data is a buffer / a file - should it be copied? */
int copy;
/**
* If `finish` is set, this data marks the end of the response.
*
* Otherwise the response will stream the data.
*/
int finish;
}
```
Arguments for the fio_http_write function.
_Symbol type:_ `type`
#### `fio_http_body_parse_callbacks_s`
```c
typedef struct {
/* ===== Primitives ===== */
/** NULL / nil was detected. Returns new object. */
void *(*on_null)(void *udata);
/** TRUE was detected. Returns new object. */
void *(*on_true)(void *udata);
/** FALSE was detected. Returns new object. */
void *(*on_false)(void *udata);
/** Number was detected. Returns new object. */
void *(*on_number)(void *udata, int64_t num);
/** Float was detected. Returns new object. */
void *(*on_float)(void *udata, double num);
/** String was detected. Returns new object. */
void *(*on_string)(void *udata, const void *data, size_t len);
/* ===== Containers ===== */
/** Array was detected. Returns context for this array. */
void *(*on_array)(void *udata, void *parent);
/** Map / Object was detected. Returns context for this map. */
void *(*on_map)(void *udata, void *parent);
/** Push value to array. Returns non-zero on error. */
int (*array_push)(void *udata, void *array, void *value);
/** Set key-value pair in map. Returns non-zero on error. */
int (*map_set)(void *udata, void *map, void *key, void *value);
/** Called when array parsing is complete. */
void (*array_done)(void *udata, void *array);
/** Called when map parsing is complete. */
void (*map_done)(void *udata, void *map);
/* ===== File Uploads (multipart) ===== */
/**
* Called when a file upload starts.
*
* Return context for this file (e.g., fd, stream, buffer).
* Return NULL to skip this file (on_file_data/on_file_done won't be called).
*/
void *(*on_file)(void *udata,
fio_str_info_s name,
fio_str_info_s filename,
fio_str_info_s content_type);
/** Called for each chunk of file data. Return non-zero to abort. */
int (*on_file_data)(void *udata, void *file, fio_buf_info_s data);
/** Called when file upload is complete. */
void (*on_file_done)(void *udata, void *file);
/* ===== Error Handling ===== */
/** Called on parse error. `partial` is the incomplete result, if any. */
void *(*on_error)(void *udata, void *partial);
/** Called to free an unused object (e.g., key when map_set fails). */
void (*free_unused)(void *udata, void *obj);
} fio_http_body_parse_callbacks_s
```
HTTP body parser callbacks.
All callbacks receive `udata` as first parameter.
Primitive callbacks return the created object as `void *`.
Container callbacks return a context for that container.
_Symbol type:_ `type`
#### `fio_http_body_parse_result_s`
```c
typedef struct {
/** Top-level parsed object (caller responsible for freeing). */
void *result;
/** Number of bytes consumed from body. */
size_t consumed;
/** Error code: 0 = success. */
int err;
} fio_http_body_parse_result_s
```
HTTP body parse result.
_Symbol type:_ `type`
#### `fio_http_header_each_s`
```c
struct fio_http_header_each_s {
fio_buf_info_s name; /* header name, or property name */
fio_buf_info_s value; /* list value, or property value */
fio_buf_info_s properties; /* property span; cursor in property iterator */
unsigned index; /* index of the current repeated header field */
unsigned is_request;
}
```
A zero-copy cursor over repeated header fields, comma-separated values and
semicolon-separated properties. `name` / `value` are length-delimited slices
borrowed from the HTTP handle, NOT necessarily NUL-terminated. Quotes and
escapes are retained, not decoded. Do not mutate headers during iteration.
`properties` holds the bounded property span (or remaining properties).
`index` and `is_request` are iterator state; do not change them.
`value.buf == NULL` marks the end; a property may have a non-NULL value
with zero length. Empty list elements and empty property names are skipped.
_Symbol type:_ `type`
#### `fio_http_sse_write_args_s`
```c
typedef struct {
/** The message's `id` data (if any). */
fio_buf_info_s id;
/** The message's `event` data (if any). */
fio_buf_info_s event;
/** The message's `data` data (if any). */
fio_buf_info_s data;
} fio_http_sse_write_args_s
```
Named arguments for fio_http_sse_write.
_Symbol type:_ `type`
### Functions
#### `fio_http_io`
```c
fio_io_s *fio_http_io(fio_http_s *)
```
Returns the IO object associated with the HTTP object (request only).
_Symbol type:_ `function`
#### `fio_http_subscribe`
```c
#define fio_http_subscribe(h, ...) \
fio_pubsub_subscribe(.io = fio_http_io(h), __VA_ARGS__)
```
Macro helper for HTTP handle pub/sub subscriptions.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_listen`
```c
fio_http_listener_s *fio_http_listen(const char *url, fio_http_settings_s settings)
```
Listens to HTTP / WebSockets / SSE connections on `url`.
_Symbol type:_ `function`
#### `fio_http_listen`
```c
#define fio_http_listen(url, ...) \
fio_http_listen(url, (fio_http_settings_s){__VA_ARGS__})
```
Listens to HTTP / WebSockets / SSE connections on `url`.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_listener_settings`
```c
fio_http_settings_s *fio_http_listener_settings(fio_http_listener_s *l)
```
Returns the a pointer to the HTTP settings associated with the listener.
_Symbol type:_ `function`
#### `fio_http_route`
```c
int fio_http_route(fio_http_listener_s *listener, const char *url, fio_http_settings_s settings)
```
Adds a route prefix to the HTTP handler.
Order of route settings is irrelevant (unless overwriting an existing route).
Matching is performed as a best-prefix match. i.e.:
- All paths match the route `"/"` (the default prefix).
- The route `"/user"` will match `"/user"` and all `"/user/..."` paths but
not `"/user..."`
- Setting `"/user/new"` as well as `"/user"` (in whatever order) will route
`"/user/new"` and `"/user/new/..."` to `"/user/new"`. Otherwise, the
`"/user"` route will continue to behave the same.
Note: the following properties are inherited (if missing) from the
default HTTP settings used to create the listener: `udata`, `on_finish`,
`on_stop`, `on_authenticate_sse`, `on_authenticate_websocket`,
`max_header_size`, `max_line_len`, `max_body_size`, `ws_max_msg_size`,
`timeout`, `ws_timeout`, `sse_timeout`, `log`, `compress_static` and
`public_folder`.
Note: TLS options are ignored.
Note: only `on_http`, `on_finish`, `udata`, the authentication
callbacks, `public_folder`, `max_age` and `compress_static` are
effective per route. All other per-route values are stored but unused -
upgraded connection callbacks (`on_open`, `on_message`, etc.), `queue`,
`log`, limits, timeouts, `compress_dynamic` and `compress_ws` are
connection-global and are read from the listener's root settings.
_Symbol type:_ `function`
#### `fio_http_route`
```c
#define fio_http_route(listener, url, ...) \
fio_http_route(listener, url, (fio_http_settings_s){__VA_ARGS__})
```
Adds a route prefix to the HTTP handler.
Order of route settings is irrelevant (unless overwriting an existing route).
Matching is performed as a best-prefix match. i.e.:
- All paths match the route `"/"` (the default prefix).
- The route `"/user"` will match `"/user"` and all `"/user/..."` paths but
not `"/user..."`
- Setting `"/user/new"` as well as `"/user"` (in whatever order) will route
`"/user/new"` and `"/user/new/..."` to `"/user/new"`. Otherwise, the
`"/user"` route will continue to behave the same.
Note: the following properties are inherited (if missing) from the
default HTTP settings used to create the listener: `udata`, `on_finish`,
`on_stop`, `on_authenticate_sse`, `on_authenticate_websocket`,
`max_header_size`, `max_line_len`, `max_body_size`, `ws_max_msg_size`,
`timeout`, `ws_timeout`, `sse_timeout`, `log`, `compress_static` and
`public_folder`.
Note: TLS options are ignored.
Note: only `on_http`, `on_finish`, `udata`, the authentication
callbacks, `public_folder`, `max_age` and `compress_static` are
effective per route. All other per-route values are stored but unused -
upgraded connection callbacks (`on_open`, `on_message`, etc.), `queue`,
`log`, limits, timeouts, `compress_dynamic` and `compress_ws` are
connection-global and are read from the listener's root settings.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_route_settings`
```c
fio_http_settings_s *fio_http_route_settings(fio_http_listener_s *l, const char *url)
```
Returns a link to the settings matching `url`, as set by `fio_http_route`
_Symbol type:_ `function`
#### `fio_http_resource_action`
```c
inline fio_http_resource_action_e fio_http_resource_action(fio_http_s *h)
```
returns expected action or `FIO_HTTP_RESOURCE_NONE` on error.
_Symbol type:_ `function`
#### `fio_http_connect`
```c
fio_io_s *fio_http_connect(const char *url, fio_http_s *h, fio_http_settings_s settings)
```
Connects to HTTP / WebSockets / SSE connections on `url`.
_Symbol type:_ `function`
#### `fio_http_connect`
```c
#define fio_http_connect(url, h, ...) \
fio_http_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to HTTP / WebSockets / SSE connections on `url`.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_websocket_connect`
```c
fio_io_s *fio_http_websocket_connect(const char *url, fio_http_s *h, fio_http_settings_s settings)
```
Connects to a WebSocket server on `url`.
A convenience wrapper around `fio_http_connect` that ensures a `ws://` or
`wss://` scheme is used in the URL (`http://` becomes `ws://`, `https://`
becomes `wss://`, a missing scheme defaults to `ws://`).
The WebSocket upgrade request / response is handled automatically by the
underlying `fio_http_connect`: on acceptance (101) the connection
switches to the WebSocket callbacks (`on_open` / `on_message` /
`on_close`); on rejection the response is routed to `settings.on_http`.
_Symbol type:_ `function`
#### `fio_http_websocket_connect`
```c
#define fio_http_websocket_connect(url, h, ...) \
fio_http_websocket_connect(url, h, (fio_http_settings_s){__VA_ARGS__})
```
Connects to a WebSocket server on `url` (see
`fio_http_websocket_connect`).
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_new`
```c
fio_http_s *fio_http_new(void)
```
Create a new fio_http_s handle.
_Symbol type:_ `function`
#### `fio_http_new_copy_request`
```c
fio_http_s *fio_http_new_copy_request(fio_http_s *old)
```
Creates a copy of an existing handle, copying only its request data.
_Symbol type:_ `function`
#### `fio_http_free`
```c
void fio_http_free(fio_http_s *)
```
Reduces an fio_http_s handle's reference count or frees it.
_Symbol type:_ `function`
#### `fio_http_dup`
```c
fio_http_s *fio_http_dup(fio_http_s *)
```
Increases an fio_http_s handle's reference count.
_Symbol type:_ `function`
#### `fio_http_destroy`
```c
fio_http_s *fio_http_destroy(fio_http_s *h)
```
Destroyed the HTTP handle object, freeing all allocated resources.
_Symbol type:_ `function`
#### `fio_http_start_time_set`
```c
void fio_http_start_time_set(fio_http_s *)
```
Collects an updated timestamp for logging purposes.
_Symbol type:_ `function`
#### `fio_http_clear_response`
```c
fio_http_s *fio_http_clear_response(fio_http_s *h, bool clear_body)
```
Clears any response data.
_Symbol type:_ `function`
#### `fio_http_udata`
```c
inline void *fio_http_udata(fio_http_s *)
```
Gets the opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata_set`
```c
inline void *fio_http_udata_set(fio_http_s *, void *)
```
Sets the opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata2`
```c
inline void *fio_http_udata2(fio_http_s *)
```
Gets the second opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_udata2_set`
```c
inline void *fio_http_udata2_set(fio_http_s *, void *)
```
Sets a second opaque user pointer associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_controller`
```c
inline fio_http_controller_s *fio_http_controller(fio_http_s *h)
```
Gets the HTTP Controller associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_controller_set`
```c
inline fio_http_controller_s *fio_http_controller_set( fio_http_s *h, fio_http_controller_s *controller)
```
Gets the HTTP Controller associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_cdata`
```c
inline void *fio_http_cdata(fio_http_s *h)
```
Returns the existing controller data (`void *` pointer).
_Symbol type:_ `function`
#### `fio_http_cdata_set`
```c
inline void *fio_http_cdata_set(fio_http_s *h, void *cdata)
```
Sets a new controller data (`void *` pointer).
_Symbol type:_ `function`
#### `fio_http_status`
```c
size_t fio_http_status(fio_http_s *)
```
Gets the status associated with the HTTP handle (response).
_Symbol type:_ `function`
#### `fio_http_status_set`
```c
size_t fio_http_status_set(fio_http_s *, size_t status)
```
Sets the status associated with the HTTP handle (response).
_Symbol type:_ `function`
#### `fio_http_method`
```c
fio_str_info_s fio_http_method(fio_http_s *)
```
Gets the method information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_method_set`
```c
fio_str_info_s fio_http_method_set(fio_http_s *, fio_str_info_s)
```
Sets the method information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_opath`
```c
fio_str_info_s fio_http_opath(fio_http_s *)
```
Gets the original / first path associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_opath_set`
```c
fio_str_info_s fio_http_opath_set(fio_http_s *, fio_str_info_s)
```
Sets the original / first path associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_path`
```c
fio_str_info_s fio_http_path(fio_http_s *)
```
Gets the path information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_path_set`
```c
fio_str_info_s fio_http_path_set(fio_http_s *, fio_str_info_s)
```
Sets the path information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_query`
```c
fio_str_info_s fio_http_query(fio_http_s *)
```
Gets the query information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_query_set`
```c
fio_str_info_s fio_http_query_set(fio_http_s *, fio_str_info_s)
```
Sets the query information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_version`
```c
fio_str_info_s fio_http_version(fio_http_s *)
```
Gets the version information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_version_set`
```c
fio_str_info_s fio_http_version_set(fio_http_s *, fio_str_info_s)
```
Sets the version information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_received_at`
```c
int64_t fio_http_received_at(fio_http_s *)
```
Gets the received_at timestamp (ms) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_received_at_set`
```c
int64_t fio_http_received_at_set(fio_http_s *, int64_t)
```
Sets the received_at timestamp (ms) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header`
```c
fio_str_info_s fio_http_request_header(fio_http_s *, fio_str_info_s name, size_t index)
```
Gets the header information associated with the HTTP handle.
Since more than a single value may be associated with a header name, the
index may be used to collect subsequent values.
An empty value is returned if no header value is available (or index is
exceeded).
_Symbol type:_ `function`
#### `fio_http_request_header_count`
```c
size_t fio_http_request_header_count(fio_http_s *, fio_str_info_s name)
```
Returns the number of headers named `name` that were received.
If `name` buffer is `NULL`, returns the number of unique headers (not the
number of unique values).
_Symbol type:_ `function`
#### `fio_http_request_header_set`
```c
fio_str_info_s fio_http_request_header_set(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_set_if_missing`
```c
fio_str_info_s fio_http_request_header_set_if_missing(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_add`
```c
fio_str_info_s fio_http_request_header_add(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Adds to the header information associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_request_header_each`
```c
size_t fio_http_request_header_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all request headers (except cookies!).
A non-zero return will stop iteration.
Returns the number of iterations performed. If `callback` is `NULL`, returns
the number of headers available (multi-value headers are counted as 1).
_Symbol type:_ `function`
#### `fio_http_body_length`
```c
size_t fio_http_body_length(fio_http_s *)
```
Gets the body (payload) length associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_body_seek`
```c
size_t fio_http_body_seek(fio_http_s *, ssize_t pos)
```
Adjusts the body's reading position. Negative values start at the end.
If `pos == SSIZE_MAX`, returns `fio_http_body_pos`.
_Symbol type:_ `function`
#### `fio_http_body_pos`
```c
size_t fio_http_body_pos(fio_http_s *h)
```
Returns the body's reading position.
_Symbol type:_ `function`
#### `fio_http_body_read`
```c
fio_str_info_s fio_http_body_read(fio_http_s *, size_t length)
```
Reads up to `length` of data from the body, returns nothing on EOF.
_Symbol type:_ `function`
#### `fio_http_body_read_until`
```c
fio_str_info_s fio_http_body_read_until(fio_http_s *, char token, size_t limit)
```
Reads from the body until finding `token`, reaching `limit` or EOF.
Note: `limit` is ignored if zero or if the remaining data is lower than
limit.
_Symbol type:_ `function`
#### `fio_http_body_expect`
```c
void fio_http_body_expect(fio_http_s *, size_t expected_length)
```
Allocates a body (payload) of (at least) the `expected_length`.
_Symbol type:_ `function`
#### `fio_http_body_write`
```c
void fio_http_body_write(fio_http_s *, const void *data, size_t len)
```
Writes `data` to the body (payload) associated with the HTTP handle.
_Symbol type:_ `function`
#### `fio_http_body_fd`
```c
int fio_http_body_fd(fio_http_s *)
```
If the body is stored in a temporary file, returns the file's handle.
Otherwise returns -1.
_Symbol type:_ `function`
#### `fio_http_body_close`
```c
void fio_http_body_close(fio_http_s *)
```
Releases body (payload) resources, closing any temporary files.
_Symbol type:_ `function`
#### `fio_http_cookie_set`
```c
int fio_http_cookie_set(fio_http_s *h, fio_http_cookie_args_s)
```
Sets a response cookie.
Returns -1 on error and 0 on success.
Note: Long cookie names and long cookie values will be considered a security
violation and an error will be returned. Many browsers and proxies impose
limits on headers and cookies, cookies often limited to 4Kb in total for both
name and value.
_Symbol type:_ `function`
#### `fio_http_cookie_set`
```c
#define fio_http_cookie_set(http___handle, ...) \
fio_http_cookie_set((http___handle), (fio_http_cookie_args_s){__VA_ARGS__})
```
Named arguments helper. See fio_http_cookie_args_s for details.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_cookie`
```c
fio_str_info_s fio_http_cookie(fio_http_s *, const char *name, size_t name_len)
```
Returns a cookie value (either received of newly set), if any.
_Symbol type:_ `function`
#### `fio_http_cookie_each`
```c
size_t fio_http_cookie_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all cookies. A non-zero return will stop iteration.
_Symbol type:_ `function`
#### `fio_http_set_cookie_each`
```c
size_t fio_http_set_cookie_each(fio_http_s *h, int (*callback)(fio_http_s *, fio_str_info_s set_cookie_header, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all response set cookies.
A non-zero return value from the callback will stop iteration.
_Symbol type:_ `function`
#### `fio_http_is_clean`
```c
int fio_http_is_clean(fio_http_s *)
```
Returns true if no HTTP headers / data was sent (a clean slate).
_Symbol type:_ `function`
#### `fio_http_is_finished`
```c
int fio_http_is_finished(fio_http_s *)
```
Returns true if the HTTP handle's response was sent.
_Symbol type:_ `function`
#### `fio_http_is_streaming`
```c
int fio_http_is_streaming(fio_http_s *)
```
Returns true if the HTTP handle's response is streaming.
_Symbol type:_ `function`
#### `fio_http_is_upgraded`
```c
int fio_http_is_upgraded(fio_http_s *h)
```
Returns true if the HTTP connection was (or should have been) upgraded.
_Symbol type:_ `function`
#### `fio_http_is_websocket`
```c
int fio_http_is_websocket(fio_http_s *)
```
Returns true if the HTTP handle refers to a WebSocket connection.
_Symbol type:_ `function`
#### `fio_http_is_sse`
```c
int fio_http_is_sse(fio_http_s *)
```
Returns true if the HTTP handle refers to an EventSource connection.
_Symbol type:_ `function`
#### `fio_http_is_freeing`
```c
int fio_http_is_freeing(fio_http_s *)
```
Returns true if handle is in the process of freeing itself.
_Symbol type:_ `function`
#### `fio_http_response_header`
```c
fio_str_info_s fio_http_response_header(fio_http_s *, fio_str_info_s name, size_t index)
```
Gets the header information associated with the HTTP handle.
Since more than a single value may be associated with a header name, the
index may be used to collect subsequent values.
An empty value is returned if no header value is available (or index is
exceeded).
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_count`
```c
size_t fio_http_response_header_count(fio_http_s *, fio_str_info_s name)
```
Returns the number of headers named `name` in the response.
If `name` buffer is `NULL`, returns the number of unique headers (not the
number of unique values).
_Symbol type:_ `function`
#### `fio_http_response_header_set`
```c
fio_str_info_s fio_http_response_header_set(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_set_if_missing`
```c
fio_str_info_s fio_http_response_header_set_if_missing(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Sets the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_add`
```c
fio_str_info_s fio_http_response_header_add(fio_http_s *, fio_str_info_s name, fio_str_info_s value)
```
Adds to the header information associated with the HTTP handle.
If the response headers were already sent, the returned value is always
empty.
_Symbol type:_ `function`
#### `fio_http_response_header_each`
```c
size_t fio_http_response_header_each(fio_http_s *, int (*callback)(fio_http_s *, fio_str_info_s name, fio_str_info_s value, void *udata), void *udata)
```
Iterates through all response headers (except cookies!).
A non-zero return will stop iteration.
_Symbol type:_ `function`
#### `fio_http_write`
```c
void fio_http_write(fio_http_s *, fio_http_write_args_s args)
```
Writes `data` to the response body associated with the HTTP handle after
sending all headers (no further headers may be sent).
_Symbol type:_ `function`
#### `fio_http_write`
```c
#define fio_http_write(http_handle, ...) \
fio_http_write(http_handle, (fio_http_write_args_s){__VA_ARGS__})
```
Named arguments helper. See fio_http_write and fio_http_write_args_s.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_finish`
```c
#define fio_http_finish(http_handle) fio_http_write(http_handle, .finish = 1)
```
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_close`
```c
void fio_http_close(fio_http_s *h)
```
Closes the associated connection after previously scheduled output drains.
Schedule the complete response before calling this (i.e., in on_finish).
_Symbol type:_ `function`
#### `fio_http_mimetype_register`
```c
int fio_http_mimetype_register(char *file_ext, size_t file_ext_len, fio_str_info_s mime_type)
```
Registers a Mime-Type to be associated with the file extension.
_Symbol type:_ `function`
#### `fio_http_mimetype`
```c
fio_str_info_s fio_http_mimetype(char *file_ext, size_t file_ext_len)
```
Finds the Mime-Type associated with the file extension (if registered).
_Symbol type:_ `function`
#### `fio_http_body_parse`
```c
fio_http_body_parse_result_s fio_http_body_parse(fio_http_s *h, const fio_http_body_parse_callbacks_s *callbacks, void *udata)
```
Parses the HTTP request body, auto-detecting content type.
Supports JSON, URL-encoded, and multipart/form-data bodies.
Calls the appropriate callbacks for each element found.
**Parameters:**
- `h` - The HTTP handle.
- `callbacks` - Parser callbacks (designed to be static const).
- `udata` - User context passed to all callbacks.
**Returns:**
- Parse result with top-level object and status.
_Symbol type:_ `function`
#### `fio_http_header_each_first`
```c
inline fio_http_header_each_s fio_http_header_each_first(fio_http_s *http_handle, int is_request, fio_str_info_s header_name)
```
Starts iterating a header's values. `header_name` is a borrowed lookup key.
Returns an iterator with `value.buf == NULL` when no value is available.
_Symbol type:_ `function`
#### `fio_http_header_each_next`
```c
inline void fio_http_header_each_next(fio_http_header_each_s *pos, fio_http_s *http_handle)
```
Advances to the next value; `http_handle` must be the original handle.
Sets `value.buf` to NULL after the last value.
_Symbol type:_ `function`
#### `fio_http_header_each_property_first`
```c
inline fio_http_header_each_s fio_http_header_each_property_first(const fio_buf_info_s *properties)
```
Starts iterating a value's bounded property span. `properties` is borrowed.
Returns an iterator with `value.buf == NULL` when no property exists.
_Symbol type:_ `function`
#### `fio_http_header_each_property_next`
```c
inline void fio_http_header_each_property_next(fio_http_header_each_s *pos)
```
Advances to the next property. A property without `=` has an empty value.
Sets `value.buf` to NULL after the last property.
_Symbol type:_ `function`
#### `fio_http_send_error_response`
```c
int fio_http_send_error_response(fio_http_s *h, size_t status)
```
Sends the requested error message and finishes the response.
_Symbol type:_ `function`
#### `fio_http_etag_is_match`
```c
int fio_http_etag_is_match(fio_http_s *h)
```
Returns true (1) if the ETag response matches an if-none-match request.
_Symbol type:_ `function`
#### `fio_http_static_file_response`
```c
int fio_http_static_file_response(fio_http_s *h, fio_str_info_s root_folder, fio_str_info_s file_name, size_t max_age)
```
Attempts to send a static file from the `root_folder` folder.
`file_name` is URL-decoded and appended to `root_folder` (path traversal
is rejected). Folders resolve to their `index` file and missing
extensions are auto-completed (`.html`, `.htm`, `.txt`, `.md` -
`FIO_HTTP_STATIC_FILE_COMPLETION`). `OPTIONS` requests are refused (a
static file is not a valid `OPTIONS` response).
`root_folder` MUST name an existing folder (use `"."` for the CWD):
the path-traversal guard rejects `..` folding, not absolute paths, so
an empty root would expose absolute paths such as `/etc/passwd`.
Settings-provided `public_folder` values are validated on listen/route
creation (non-existent folders are rejected); direct callers MUST pass
a valid, non-empty root. Symlinks inside the root are followed (like
nginx's default) - applications requiring strict containment should
keep the root free of symlinks.
Handles conditional requests (`ETag` / `If-None-Match` -> 304), single
`Range` requests (206 / 416, always served identity), and `HEAD`
requests; sets `Last-Modified`, `Accept-Ranges: bytes`, and (when
`max_age` is non-zero) `Cache-Control: max-age=...`.
Pre-compressed variants (`file.br`, `file.zstd`, `file.gz`, `file.zip`,
in this preference order) are served when accepted by the client,
present on disk, and fresh; with `compress_static` enabled (the matching
route's settings, or the `FIO_HTTP_CFLAG_COMPRESS_STATIC` cflag on
detached handles), missing `.br` / `.gz` variants are also created on
demand.
On success the response is complete and 0 is returned. On failure -1 is
returned and the application should handle the request itself.
_Symbol type:_ `function`
#### `fio_http_status2str`
```c
fio_str_info_s fio_http_status2str(size_t status)
```
Returns a human readable string related to the HTTP status number.
_Symbol type:_ `function`
#### `fio_http_write_log`
```c
void fio_http_write_log(fio_http_s *h)
```
Logs an HTTP (response) to STDOUT.
_Symbol type:_ `function`
#### `fio_http_from`
```c
int fio_http_from(fio_str_info_s *dest, const fio_http_s *h)
```
Writes peer address to `dest` starting with the `forwarded` header, with a
fallback to actual socket address and a final fallback to `"[unknown]"`.
If `unknown` is returned, the function returns -1. if `dest` capacity is too
small, the number of bytes required will be returned.
If all goes well, this function returns 0.
_Symbol type:_ `function`
#### `fio_http_date`
```c
fio_str_info_s fio_http_date(uint64_t now_in_seconds)
```
_Symbol type:_ `function`
#### `fio_http_log_time`
```c
fio_str_info_s fio_http_log_time(uint64_t now_in_seconds)
```
_Symbol type:_ `function`
#### `fio_http_websocket_requested`
```c
int fio_http_websocket_requested(fio_http_s *)
```
Returns non-zero if request headers ask for a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_accepted`
```c
int fio_http_websocket_accepted(fio_http_s *h)
```
Returns non-zero if the response accepts a WebSocket upgrade request.
_Symbol type:_ `function`
#### `fio_http_upgrade_websocket`
```c
void fio_http_upgrade_websocket(fio_http_s *)
```
Sets response data to agree to a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_set_request`
```c
void fio_http_websocket_set_request(fio_http_s *)
```
Sets request data to request a WebSockets Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_requested`
```c
int fio_http_sse_requested(fio_http_s *)
```
Returns non-zero if request headers ask for an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_accepted`
```c
int fio_http_sse_accepted(fio_http_s *h)
```
Returns non-zero if the response accepts an SSE request.
_Symbol type:_ `function`
#### `fio_http_upgrade_sse`
```c
void fio_http_upgrade_sse(fio_http_s *)
```
Sets response data to agree to an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_sse_set_request`
```c
void fio_http_sse_set_request(fio_http_s *)
```
Sets request data to request an EventSource (SSE) Upgrade.
_Symbol type:_ `function`
#### `fio_http_websocket_write`
```c
int fio_http_websocket_write(fio_http_s *h, const void *buf, size_t len, uint8_t is_text)
```
Writes a WebSocket message. Fails if connection wasn't upgraded yet.
_Symbol type:_ `function`
#### `fio_http_on_message_set`
```c
int fio_http_on_message_set(fio_http_s *h, void (*on_message)(fio_http_s *, fio_buf_info_s, uint8_t))
```
Sets a specific on_message callback for this connection.
Returns -1 on error (i.e., upgrade still in negotiation).
_Symbol type:_ `function`
#### `fio_http_sse_write`
```c
int fio_http_sse_write(fio_http_s *h, fio_http_sse_write_args_s args)
```
Writes an SSE message (UTF-8). Fails if connection wasn't upgraded yet.
_Symbol type:_ `function`
#### `fio_http_sse_write`
```c
#define fio_http_sse_write(h, ...) \
fio_http_sse_write((h), ((fio_http_sse_write_args_s){__VA_ARGS__}))
```
Writes an SSE message (UTF-8). Fails if connection wasn't upgraded yet.
_Note:_ this may be a macro only / macro wrapper for a function.
_Symbol type:_ `macro`
#### `fio_http_settings`
```c
fio_http_settings_s *fio_http_settings(fio_http_s *)
```
Returns the HTTP settings associated with the HTTP object, if any.
_Symbol type:_ `function`
-----------------------------------------------------
# facil.io 0.7.x — complete documentation
# 0.7.x HTTP / WebSockets Server Documentation
facil.io includes an HTTP/1.1 and WebSocket server / framework that could be used to author HTTP and WebSocket services, including REST applications, micro-services, etc.
Note that, currently, only HTTP/1.1 is supported. Support for HTTP/2 is planned for future versions and could be implemented as a custom protocol until such time.
To use the facil.io HTTP and WebSocket API, include the file `http.h`
## Listening to HTTP Connections
#### `http_listen`
```c
intptr_t http_listen(const char *port, const char *binding,
struct http_settings_s);
Listens to HTTP connections at the specified `port` and `binding`.
#define http_listen(port, binding, ...) \
http_listen((port), (binding), (struct http_settings_s){__VA_ARGS__})
```
The `http_listen` function is shadowed by the `http_listen` MACRO, which allows the function to accept "named arguments", i.e.:
```c
/* Assuming we defined the HTTP request handler: */
static void on_http_request(http_s *h);
// ...
if (http_listen("3000", NULL,
.on_request = on_http_request,
.public_folder = "www" ) == -1) {
perror("No listening socket available on port 3000");
exit(-1);
}
```
In addition to the `port` and `address` argument (explained in [`fio_listen`](https://facil.io/0.7.x/fio/#fio_listen)), the following arguments are supported:
* `on_request`:
Callback for normal HTTP requests.
// callback example:
void on_request(http_s *request);
* `on_upgrade`:
Callback for Upgrade and EventSource (SSE) requests.
Server Sent Events (SSE) / EventSource requests set the `requested_protocol` string to `"sse"`. Other protocols (i.e., WebSockets) are represented exactly the same was as the client requested them (be aware or lower case vs. capitalized representations).
// callback example:
void on_upgrade(http_s *request, char *requested_protocol, size_t len);
* `on_response`:
This callback is ignored for HTTP server mode and is only called when a response (not a request) is received. On server connections, this would normally indicate a protocol error.
// callback example:
void on_response(http_s *response);
* `on_finish`:
This (optional) callback will be called when the HTTP service closes. The `setting` pointer will point to the named arguments passed to the `http_listen` function.
// callback example:
void on_finish(struct http_settings_s *settings);
* `udata`:
Opaque user data. facil.io will ignore this field, but you can use it.
// type:
void *udata;
* `public_folder`:
A public folder for file transfers - allows to circumvent any application layer logic and simply serve static files.
The static file service supports automatic `gz` pre-compressed file alternatives.
// type:
const char *public_folder;
* `public_folder_length`:
The length of the public_folder string.
// type:
size_t public_folder_length;
* `tls`:
A pointer to a `fio_tls_s` object, for [SSL/TLS support](https://facil.io/0.7.x/fio_tls/) (fio_tls.h).
// type:
void *tls;
* `max_header_size`:
The maximum number of bytes allowed for the request string (method, path, query), header names and fields.
Defaults to 32Kib (which is about 4 times more than I would recommend).
This reflects the total overall size. On HTTP/1.1, each header line (name + value pair) is also limited to a hard-coded HTTP_MAX_HEADER_LENGTH bytes.
// type:
size_t max_header_size;
* `max_body_size`:
The maximum size, in bytes, for an HTTP request's body (posting / downloading).
Defaults to 50Mb (1024 * 1024 * 50).
// type:
size_t max_body_size;
* `max_clients`:
The maximum number of clients that are allowed to connect concurrently. This value's default setting is usually for the best.
The default value is computed according to the server's capacity, leaving some breathing room for other network and disk operations.
Note: clients, by the nature of socket programming, are counted according to their internal file descriptor (`fd`) value. Open files and other sockets count towards a server's limit.
// type:
intptr_t max_clients;
* `ws_max_msg_size`:
The maximum WebSocket message size/buffer (in bytes) for WebSocket connections. Defaults to 250KB (250 * 1024).
// type:
size_t ws_max_msg_size;
* `timeout`:
The maximum WebSocket message size/buffer (in bytes) for WebSocket connections. Defaults to 250KB (250 * 1024).
// type:
uint8_t timeout;
* `ws_timeout`:
Timeout for the WebSocket connections, a ping will be sent whenever the timeout is reached.
Defaults to 40 seconds.
Connections are only closed when a ping cannot be sent (the network layer fails). Pongs are ignored.
// type:
uint8_t ws_timeout;
* `log`:
Logging flag - set to TRUE to log HTTP requests.
Defaults to 0 (false).
// type:
uint8_t log;
* `is_client`:
A read only flag set automatically to indicate the protocol's mode.
This is ignored by the `http_listen` function but can be accessed through the `on_finish` callback and the [`http_settings`](#http_settings) function.
* `reserved*`:
Reserved for future use.
// type:
intptr_t reserved1;
intptr_t reserved2;
intptr_t reserved3;
Returns -1 on error and the socket's `uuid` on success.
The `on_finish` callback is always called (even on errors).
## Connecting to HTTP as a Client
#### `http_connect`
```c
intptr_t http_connect(const char *address, struct http_settings_s);
#define http_connect(address, ...) \
http_connect((address), (struct http_settings_s){__VA_ARGS__})
```
Connects to an HTTP server as a client.
This function accepts the same arguments as the [`http_listen`](#http_listen) function (though some of hem will not be relevant for a client connection).
Upon a successful connection, the `on_response` callback is called with an empty `http_s*` handler (status == 0).
Use the HTTP Handler API to set it's content and send the request to the server. The second time the `on_response` function is called, the `http_s` handle will contain the actual response.
The `address` argument should contain a full URL style address for the server. i.e.:
"http:/www.example.com:8080/"
If an `address` includes a path or query data, they will be automatically attached (both of them) to the HTTP handle's `path` property. i.e.
"http:/www.example.com:8080/my_path?foo=bar"
// will result in:
fiobj_obj2cstr(h->path).data; //=> "/my_path?foo=bar"
To open a WebSocket connection, it's possible to use the `ws` protocol signature. However, it would be better to use the [`websocket_connect`](#websocket_connect) function instead.
Returns -1 on error and the socket's uuid on success.
The `on_finish` callback is always called.
## The HTTP Data Handle (Request / Response)
HTTP request and response data is manages using the `http_s` structure type, which is defined as follows:
```c
typedef struct {
struct {
void *vtbl;
uintptr_t flag;
FIOBJ out_headers;
} private_data;
struct timespec received_at;
FIOBJ method;
FIOBJ status_str;
FIOBJ version;
uintptr_t status;
FIOBJ path;
FIOBJ query;
FIOBJ headers;
FIOBJ cookies;
FIOBJ params;
FIOBJ body;
void *udata;
} http_s;
```
The `http_s` data is NOT thread safe and can only be accessed safely from within the HTTP callbacks (`on_request`, `on_response` and the `http_defer` callback)
### HTTP Handle Data Access
HTTP data can be accessed using the HTTP handle structure fields.
For example:
```c
/* Collects a temporary reference to the Host header. Don't free the reference.*/
FIOBJ host = fiobj_hash_get(h->headers, HTTP_HEADER_HOST);
```
#### `h->received_at`
```c
struct timespec received_at;
```
A time merker indicating when the request was received.
#### `h->method`
```c
FIOBJ method;
```
A [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) containing the HTTP method string.
facil.io accepts non-standard methods as well as the standard GET, PUT, POST, etc' HTTP methods.
#### `h->status`
```c
uintptr_t status;
```
The status used for the response (or if the object is a response).
When sending a request, the status should be set to 0.
#### `h->status_str`
```c
FIOBJ status_str;
```
A [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) containing the HTTP status string, for response objects (client mode response).
#### `h->version`
```c
FIOBJ version;
```
A [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) containing the HTTP version string, if any.
#### `h->path`
```c
FIOBJ path;
```
A [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) containing the request path string, if any.
#### `h->query`
```c
FIOBJ query;
```
A [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) containing the request query string, if any.
#### `h->headers`
```c
FIOBJ headers;
```
A [FIOBJ Hash Map](https://facil.io/0.7.x/fiobj_hash/) containing the received header data as either a [FIOBJ String](https://facil.io/0.7.x/fiobj_str/) or an [FIOBJ Array](https://facil.io/0.7.x/fiobj_ary/).
When a header is received multiple times (such as cookie headers), an Array of Strings will be used instead of a single String.
#### `h->cookies`
```c
FIOBJ cookies;
```
A placeholder for a hash of cookie data.
The [FIOBJ Hash Map](https://facil.io/0.7.x/fiobj_hash/) will be initialized when parsing the request using [`http_parse_cookies`](#http_parse_cookies).
#### `h->params`
```c
FIOBJ params;
```
A placeholder for a hash of request data.
The [FIOBJ Hash Map](https://facil.io/0.7.x/fiobj_hash/) will be initialized when parsing the request using [`http_parse_body`](#http_parse_body) and [`http_parse_query`](#http_parse_query).
#### `h->body`
```c
FIOBJ body;
```
A [FIOBJ Data](https://facil.io/0.7.x/fiobj_data/) reader for body data (might be a temporary file, a string or NULL).
#### `h->udata`
```c
void *udata;
```
An opaque user data pointer, which can be used **before** calling [`http_defer`](#http_defer) in order to retain persistent application information across events.
#### `h->private_data`
```c
struct {
void *vtbl;
uintptr_t flag;
FIOBJ out_headers;
} private_data;
```
Private data shouldn't be accessed directly. However, if you need to access the data, limit yourself to the `out_headers` field. The `vtbl` and `flag` should **never* be altered as.
The out headers are set using the [`http_set_header`](#http_set_header), [`http_set_header2`](#http_set_header2), and [`http_set_cookie`](#http_set_cookie) functions.
Reading the outgoing headers is possible by directly accessing the [Hash Map](https://facil.io/0.7.x/fiobj_hash/) data. However, writing data to the Hash should be avoided.
### Connection Information
#### `http_settings`
```c
struct http_settings_s *http_settings(http_s *h);
```
Returns the settings used to setup the connection or NULL on error.
#### `http_peer_addr`
```c
fio_str_info_s http_peer_addr(http_s *h);
```
Returns the direct address of the connected peer (most possibly an intermediary / proxy server).
NOTE: it is my hope that a future variation of this function will return a best guess at the actual client's address
### Setting Headers and Cookies
#### `http_set_header`
```c
int http_set_header(http_s *h, FIOBJ name, FIOBJ value);
```
Sets an outgoing header, taking ownership of the value object, but NOT the name object (so name objects could be reused in future responses).
Returns -1 on error and 0 on success.
#### `http_set_header2`
```c
int http_set_header2(http_s *h, fio_str_info_s name, fio_str_info_s value);
```
Sets an outgoing header, copying the data.
Returns -1 on error and 0 on success.
#### `http_set_cookie`
```c
int http_set_cookie(http_s *h, http_cookie_args_s);
#define http_set_cookie(http___handle, ...) \
http_set_cookie((http___handle), (http_cookie_args_s){__VA_ARGS__})
```
Sets an outgoing **response** cookie.
To set a *request* cookie, simply add the data to a header named `"cookie"`.
The `http_set_cookie` function is shadowed by the `http_set_cookie` MACRO, which allows the function to accept "named arguments", i.e.:
```c
http_set_cookie(request, .name = "my_cookie", .value = "data");
```
In addition to the handle argument (`http_s *`), the following arguments are supported:
* `name`:
The cookie's name (Symbol).
// type:
const char *name;
* `value`:
The cookie's value. Leave the value blank (NULL) to delete cookie.
// type:
const char *value;
* `domain`:
The cookie's domain (optional).
// type:
const char *domain;
* `path`:
The cookie's path (optional).
// type:
const char *path;
* `name_len`:
The cookie name's length in bytes or a terminating NUL will be assumed.
// type:
size_t name_len;
* `value_len`:
The cookie value's length in bytes or a terminating NULL will be assumed.
// type:
size_t value_len;
* `domain_len`:
The cookie domain's length in bytes or a terminating NULL will be assumed.
// type:
size_t domain_len;
* `path_len`:
The cookie path's length in bytes or a terminating NULL will be assumed.
// type:
size_t path_len;
* `max_age`:
Max Age (how long should the cookie persist), in seconds (0 == session).
// type:
int max_age;
* `secure`:
Limit cookie to secure connections.
// type:
unsigned secure : 1;
* `http_only`:
Limit cookie to HTTP (intended to prevent JavaScript access/hijacking).
// type:
unsigned http_only : 1;
Returns -1 on error and 0 on success.
**Note**: Long cookie names and long cookie values will be considered a security violation and an error will be returned. It should be noted that most proxies and servers will refuse long cookie names or values and many impose total header lengths (including cookies) of ~8Kib.
### Sending a Response / Request
#### `http_finish`
```c
void http_finish(http_s *h);
```
Sends the response headers for a header only response.
**Important**: After this function is called, the `http_s` object is no longer valid.
#### `http_send_body`
```c
int http_send_body(http_s *h, void *data, uintptr_t length);
```
Sends the response headers and body.
**Note**: The body is *copied* to the HTTP stream and it's memory should be
freed by the calling function.
Returns -1 on error and 0 on success.
**Important**: After this function is called, the `http_s` object is no longer valid.
#### `http_sendfile`
```c
int http_sendfile(http_s *h, int fd, uintptr_t length, uintptr_t offset);
```
Sends the response headers and the specified file (the response's body).
The file is closed automatically.
Returns -1 on error and 0 on success.
**Important**: After this function is called, the `http_s` object is no longer valid.
#### `http_sendfile2`
```c
int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
const char *encoded, size_t encoded_len);
```
Sends the response headers and the specified file (the response's body).
The `local` and `encoded` strings will be joined into a single string that represent the file name. Either or both of these strings can be empty.
The `encoded` string will be URL decoded while the `local` string will used as is.
Returns 0 on success. A success value WILL CONSUME the `http_s` handle (it will become invalid).
Returns -1 on error (The `http_s` handle should still be used).
**Important**: After this function is called, the `http_s` object is no longer valid.
#### `http_send_error`
```c
int http_send_error(http_s *h, size_t error_code);
```
Sends an HTTP error response.
Returns -1 on error and 0 on success.
**Important**: After this function is called, the `http_s` object is no longer valid.
### Push Promise (future HTTP/2 support)
**Note**: HTTP/2 isn't implemented yet and these functions will simply fail.
#### `http_push_data`
```c
int http_push_data(http_s *h, void *data, uintptr_t length, FIOBJ mime_type);
```
Pushes a data response when supported (HTTP/2 only).
Returns -1 on error and 0 on success.
#### `http_push_file`
```c
int http_push_file(http_s *h, FIOBJ filename, FIOBJ mime_type);
```
Pushes a file response when supported (HTTP/2 only).
If `mime_type` is NULL, an attempt at automatic detection using `filename` will be made.
Returns -1 on error and 0 on success.
### Rescheduling the HTTP event
#### `http_pause`
```c
void http_pause(http_s *h, void (*task)(http_pause_handle_s *http));
```
Pauses the request / response handling and INVALIDATES the current `http_s` handle (no `http` functions can be called).
The `http_resume` function MUST be called (at some point) using the opaque `http` pointer given to the callback `task`.
The opaque `http_pause_handle_s` pointer is only valid for a single call to `http_resume` and can't be used by any regular `http` function (it's a different data type).
Note: the current `http_s` handle will become invalid once this function is called and it's data might be deallocated, invalid or used by a different thread.
#### `http_resume`
```c
void http_resume(http_pause_handle_s *http, void (*task)(http_s *h),
void (*fallback)(void *udata));
```
Resumes a request / response handling within a task and INVALIDATES the current `http_s` handle.
The `task` MUST call one of the `http_send_*`, `http_finish`, or `http_pause`functions.
The (optional) `fallback` will receive the opaque `udata` that was stored in the HTTP handle and can be used for cleanup.
Note: `http_resume` can only be called after calling `http_pause` and entering it's task.
Note: the current `http_s` handle will become invalid once this function is called and it's data might be deallocated, invalidated or used by a different thread.
#### `http_paused_udata_get`
```c
void *http_paused_udata_get(http_pause_handle_s *http);
```
Returns the `udata` associated with the paused opaque handle
#### `http_paused_udata_set`
```c
void *http_paused_udata_set(http_pause_handle_s *http, void *udata);
```
Sets the `udata` associated with the paused opaque handle, returning the old value.
### Deeper HTTP Data Parsing
The HTTP extension's initial HTTP parser parses the protocol, but not the HTTP data. This allows improved performance when parsing the data isn't necessary.
However, sometimes an application will want to parse a requests (or a response's) content, cookies or query parameters, converting it into a complex object.
This allows an application to convert a String data such as `"user[name]=Joe"` to a nested Hash Map, where the `params` Hash Map's key `user` maps to a nested Hash Map with the key `name` (and the value `"Joe"`, as described by [`http_add2hash`](#http_add2hash))).
#### `http_parse_body`
```c
int http_parse_body(http_s *h);
```
Attempts to decode the request's body using the [`http_add2hash`](#http_add2hash) scheme.
Supported body types include:
* application/x-www-form-urlencoded
* application/json
* multipart/form-data
This should be called before `http_parse_query`, in order to support JSON data.
If the JSON data isn't an object, it will be saved under the key "JSON" in the `params` hash.
If the `multipart/form-data` type contains JSON files, they will NOT be parsed (they will behave like any other file, with `data`, `type` and `filename` keys assigned). This allows non-object JSON data (such as array) to be handled by the app.
#### `http_parse_query`
```c
void http_parse_query(http_s *h);
```
Parses the query part of an HTTP request/response. Uses [`http_add2hash`](#http_add2hash).
This should be called after the `http_parse_body` function, just in case the
body is a JSON object that doesn't have a Hash Map at it's root.
#### `http_parse_cookies`
```c
void http_parse_cookies(http_s *h, uint8_t is_url_encoded);
```
Parses any Cookie / Set-Cookie headers, using the [`http_add2hash`](#http_add2hash) scheme.
#### `http_add2hash`
```c
int http_add2hash(FIOBJ dest, char *name, size_t name_len, char *value,
size_t value_len, uint8_t encoded);
```
Adds a named parameter to the hash, converting a string to an object and resolving nesting references and URL decoding if required. i.e.:
* "name[]" references a nested Array (nested in the Hash).
* "name[key]" references a nested Hash.
* "name[][key]" references a nested Hash within an array. Hash keys will be unique (repeating a key advances the array).
* These rules can be nested (i.e. "name[][key1][][key2]...")
* "name[][]" is an error (there's no way for the parser to analyze dimensions)
Note: names can't begin with `"["` or end with `"]"` as these are reserved characters.
#### `http_add2hash2`
```c
int http_add2hash2(FIOBJ dest, char *name, size_t name_len, FIOBJ value,
uint8_t encoded);
```
Same as [`http_add2hash`](#http_add2hash), using an existing object.
### Miscellaneous HTTP Helpers
#### `http_status2str`
```c
fio_str_info_s http_status2str(uintptr_t status);
```
Returns a human readable string representing the HTTP status number.
#### `http_hijack`
```c
intptr_t http_hijack(http_s *h, fio_str_info_s *leftover);
```
Hijacks the socket away from the HTTP protocol and away from facil.io.
It's possible to hijack the socket and than reconnect it to a new protocol object.
It's possible to call `http_finish` immediately after calling `http_hijack` in order to send any outgoing headers before the hijacking is complete.
If any additional HTTP functions are called after the hijacking, the protocol object might attempt to continue reading data from the buffer.
Returns the underlining socket connection's uuid. If `leftover` isn't NULL, it will be populated with any remaining data in the HTTP buffer (the data will be automatically deallocated, so copy the data when in need).
**WARNING**: this isn't a good way to handle HTTP connections, especially as HTTP/2 enters the picture. To implement Server Sent Events consider calling [`http_upgrade2sse`](#http_upgrade2sse) instead.
#### `http_req2str`
```c
FIOBJ http_req2str(http_s *h);
```
Returns a String object representing the unparsed HTTP request (HTTP version
is capped at HTTP/1.1). Mostly usable for proxy usage and debugging.
#### `http_write_log`
```c
void http_write_log(http_s *h);
```
Writes a log line to `stderr` about the request / response object.
This function is called automatically if the `.log` setting is enabled.
## WebSockets
### WebSocket Upgrade From HTTP (Server)
#### `http_upgrade2ws`
```c
int http_upgrade2ws(http_s *http, websocket_settings_s);
#define http_upgrade2ws(http, ...) \
http_upgrade2ws((http), (websocket_settings_s){__VA_ARGS__})
```
Upgrades an HTTP/1.1 connection to a WebSocket connection.
The `http_upgrade2ws` function is shadowed by the `http_upgrade2ws` MACRO, which allows the function to accept "named arguments".
In addition to the `http_s` argument, the following named arguments can be used:
* `on_open`:
The (optional) `on_open` callback will be called once the WebSocket connection is established and before is is registered with `facil`, so no `on_message` events are raised before `on_open` returns.
// callback example:
void on_open(ws_s *ws);
* `on_message`:
The (optional) `on_message` callback will be called whenever a webSocket message is received for this connection.
The data received points to the WebSocket's message buffer and it will be overwritten once the function exits (it cannot be saved for later, but it can be copied).
// callback example:
void on_message(ws_s *ws, fio_str_info_s msg, uint8_t is_text);
* `on_ready`:
The (optional) `on_ready` callback will be after a the underlying socket's buffer changes it's state from full to empty.
If the socket's buffer is never used, the callback is never called.
// callback example:
void on_ready(ws_s *ws);
* `on_shutdown`:
The (optional) on_shutdown callback will be called if a WebSocket connection is still open while the server is shutting down (called before `on_close`).
// callback example:
void on_shutdown(ws_s *ws);
* `on_close`:
The `uuid` is the connection's unique ID that can identify the WebSocket. A value of `uuid == -1` indicates the WebSocket connection wasn't established (an error occurred).
The `udata` is the user data as set during the upgrade or using the `websocket_udata_set` function.
// callback example:
void on_close(intptr_t uuid, void *udata);
* `udata`:
Opaque user data.
// type:
void *udata;
This function will end the HTTP stage of the connection and attempt to "upgrade" to a WebSockets connection.
The `http_s` handle will be invalid after this call and the `udata` will be set to the new WebSocket `udata`.
A client connection's `on_finish` callback will be called (since the HTTP stage has finished).
Returns 0 on success or -1 on error.
**NOTE**:
The type used by some of the callbacks (`ws_s`) is an opaque WebSocket handle and has no relationship with the named arguments used in this function cal. It is only used to identify a WebSocket connection.
Similar to an `http_s` handle, it is only valid within the scope of the specific connection (the callbacks / tasks) and shouldn't be stored or accessed otherwise.
### WebSocket Connections (Client)
#### `websocket_connect`
```c
int websocket_connect(const char *url, websocket_settings_s settings);
#define websocket_connect(url, ...) \
websocket_connect((address), (websocket_settings_s){__VA_ARGS__})
```
Connects to a WebSocket service according to the provided address.
This is a somewhat naive connector object, it doesn't perform any authentication or other logical handling. However, it's quire easy to author a complext authentication logic using a combination of `http_connect` and `http_upgrade2ws`.
In addition to the `url` address, this function accepts the same named arguments as [`http_upgrade2ws`](#http_upgrade2ws).
Returns the `uuid` for the future WebSocket on success.
Returns -1 on error;
### WebSocket Connection Management (write / close)
#### `websocket_write`
```c
int websocket_write(ws_s *ws, fio_str_info_s msg, uint8_t is_text);
```
Writes data to the WebSocket. Returns -1 on failure (0 on success).
#### `websocket_close`
```c
void websocket_close(ws_s *ws);
```
Closes a WebSocket connection. */
### WebSocket Pub/Sub
#### `websocket_subscribe`
```c
uintptr_t websocket_subscribe(struct websocket_subscribe_s args);
#define websocket_subscribe(, ...) \
websocket_subscribe((struct websocket_subscribe_s){.ws = ws_handle, __VA_ARGS__})
```
Subscribes to a pub/sub channel for, allowing for direct message deliverance when the `on_message` callback is missing.
To unsubscribe from the channel, use [`websocket_unsubscribe`](websocket_unsubscribe) (NOT
`fio_unsubscribe`).
The `websocket_subscribe` function is shadowed by the `websocket_subscribe` MACRO, which allows the function to accept "named arguments".
In addition to the `ws_s *` argument, the following named arguments can be used:
* `channel`:
The channel name used for the subscription. If missing, an empty string is assumed to be the channel name.
// type:
fio_str_info_s channel;
* `on_message`:
An optional callback to be called when a pub/sub message is received.
If missing, Data is directly written to the WebSocket connection.
// callback example:
void on_message(ws_s *ws, fio_str_info_s channel,
fio_str_info_s msg, void *udata);
* `on_unsubscribe`:
An optional callback to be called after the subscription was canceled. This should be used for any required cleanup.
// callback example:
void on_unsubscribe(void *udata);
* `match`:
A callback for pattern matching, as described in [`fio_subscribe`](https://facil.io/0.7.x/fio/#fio_subscribe).
Note that only the `FIO_MATCH_GLOB` matching function (or NULL for no pattern matching) is safe to use with the Redis extension.
// callback example:
int foo_bar_match_fn(fio_str_info_s pattern, fio_str_info_s channel);
// type:
fio_match_fn match;
* `udata`:
Opaque user data.
// type:
void *udata;
* `force_binary`:
When using direct message forwarding (no `on_message` callback), this indicates if messages should be sent to the client as binary blobs, which is the safest approach.
By default, facil.io will test for UTF-8 data validity and send the data as text if it's a valid UTF-8. Messages above ~32Kb might be assumed to be binary rather than tested.
// type:
unsigned force_binary : 1;
* `force_text`:
When using direct message forwarding (no `on_message` callback), this indicates if messages should be sent to the client as UTF-8 text.
By default, facil.io will test for UTF-8 data validity and send the data as text if it's a valid UTF-8. Messages above ~32Kb might be assumed to be binary rather than tested.
`force_binary` has precedence over `force_text`.
// type:
unsigned force_text : 1;
Returns a subscription ID on success and 0 on failure.
All subscriptions are automatically canceled and freed once the WebSocket is closed.
#### `websocket_unsubscribe`
```c
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
```
Unsubscribes from a channel.
Failures are silent.
All subscriptions are automatically revoked once the WebSocket is closed. So
only use this function to unsubscribe while the WebSocket is open.
#### `websocket_optimize4broadcasts`
```c
void websocket_optimize4broadcasts(intptr_t type, int enable);
```
Enables (or disables) any of the following broadcast optimizations:
* `WEBSOCKET_OPTIMIZE_PUBSUB` - Optimize generic Pub/Sub WebSocket broadcasts.
#define WEBSOCKET_OPTIMIZE_PUBSUB (-32)
* `WEBSOCKET_OPTIMIZE_PUBSUB_TEXT` - Optimize text Pub/Sub WebSocket broadcasts.
#define WEBSOCKET_OPTIMIZE_PUBSUB_TEXT (-33)
* `WEBSOCKET_OPTIMIZE_PUBSUB_BINARY` - Optimize binary Pub/Sub WebSocket broadcasts.
#define WEBSOCKET_OPTIMIZE_PUBSUB_BINARY (-34)
This is normally performed automatically by the `websocket_subscribe` function. However, this function is provided for enabling the pub/sub meta-data based optimizations for external connections / subscriptions.
The pub/sub metadata type ID will match the optimnization type requested (i.e., `WEBSOCKET_OPTIMIZE_PUBSUB`) and the optimized data is a FIOBJ String containing a pre-encoded WebSocket packet ready to be sent. i.e.:
```c
FIOBJ pre_wrapped = (FIOBJ)fio_message_metadata(msg,
WEBSOCKET_OPTIMIZE_PUBSUB);
fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
```
**Note**: to disable an optimization it should be disabled the same amount of times it was enabled - multiple optimization enablements for the same type are merged, but reference counted (disabled when reference is zero).
### WebSocket Data
#### `websocket_udata_get`
```c
void *websocket_udata_get(ws_s *ws);
```
Returns the opaque user data associated with the WebSocket.
#### `websocket_udata_set`
```c
void *websocket_udata_set(ws_s *ws, void *udata);
```
Sets the opaque user data associated with the WebSocket.
Returns the old value, if any.
#### `websocket_uuid`
```c
intptr_t websocket_uuid(ws_s *ws);
```
Returns the underlying socket UUID.
This is only relevant for collecting the protocol object from outside of WebSocket events, as the socket shouldn't be written to.
#### `websocket_is_client`
```c
uint8_t websocket_is_client(ws_s *ws);
```
Returns 1 if the WebSocket connection is in Client mode (connected to a remote server) and 0 if the connection is in Server mode (a connection established using facil.io's HTTP server).
### WebSocket Helpers
#### `websocket_attach`
```c
void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
websocket_settings_s *args, void *data, size_t length);
```
Used **internally**: attaches the WebSocket protocol to the uuid.
## EventSource / Server Sent Events (SSE)
### EventSource (SSE) Connection Management
#### `http_upgrade2sse`
```c
int http_upgrade2sse(http_s *h, http_sse_s);
#define http_upgrade2sse(h, ...) \
http_upgrade2sse((h), (http_sse_s){__VA_ARGS__})
```
Upgrades an HTTP connection to an EventSource (SSE) connection.
The `http_s` handle will be invalid after this call.
On HTTP/1.1 connections, this will preclude future requests using the same connection.
The `http_upgrade2sse` function is shadowed by the `http_upgrade2sse` MACRO, which allows the function to accept "named arguments", much like `http_listen`. i.e.:
```c
on_open_sse(sse_s * sse) {
http_sse_subscribe(sse, .channel = CHANNEL_NAME); // a simple subscription example
}
on_upgrade(http_s* h) {
http_upgrade2sse(h, .on_open = on_open_sse);
}
```
In addition to the `http_s` argument, the following arguments are supported:
* `on_open`:
The (optional) `on_open` callback will be called once the EventSource connection is established.
The `http_sse_s` pointer passed to the callback contains a copy of the named arguments passed to the `http_upgrade2sse` function.
// callback example:
void on_open(http_sse_s *sse);
* `on_ready`:
The (optional) `on_ready` callback will be called every time the underlying socket's buffer changes it's state to empty.
If the socket's buffer is never used, the callback might never get called.
The `http_sse_s` pointer passed to the callback contains a copy of the named arguments passed to the `http_upgrade2sse` function.
// callback example:
void on_ready(http_sse_s *sse);
* `on_shutdown`:
The (optional) `on_shutdown` callback will be called if a connection is still open while the server is shutting down (called before `on_close`).
The `http_sse_s` pointer passed to the callback contains a copy of the named arguments passed to the `http_upgrade2sse` function.
// callback example:
void on_shutdown(http_sse_s *sse);
* `on_close`:
The (optional) `on_close` callback will be called once a connection is terminated or failed to be established.
The `udata` passed to the `http_upgrade2sse` function is available through the `http_sse_s` pointer (`sse->udata`).
// callback example:
void on_close(http_sse_s *sse);
* `udata`:
Opaque user data.
// type:
void *udata;
#### `http_sse_set_timout`
```c
void http_sse_set_timout(http_sse_s *sse, uint8_t timeout);
```
Sets the ping interval for SSE connections.
#### `http_sse_close`
```c
int http_sse_close(http_sse_s *sse);
```
Closes an EventSource (SSE) connection.
### EventSource (SSE) Pub/Sub
#### `http_sse_subscribe`
```c
uintptr_t http_sse_subscribe(http_sse_s *sse,
struct http_sse_subscribe_args args);
#define http_sse_subscribe(sse, ...) \
http_sse_subscribe((sse), (struct http_sse_subscribe_args){__VA_ARGS__})
```
Subscribes to a pub/sub channel for, allowing for direct message deliverance when the `on_message` callback is missing.
To unsubscribe from the channel, use [`http_sse_unsubscribe`](http_sse_unsubscribe) (NOT
`fio_unsubscribe`).
All subscriptions are automatically canceled and freed once the connection is closed.
The `http_sse_subscribe` function is shadowed by the `http_sse_subscribe` MACRO, which allows the function to accept "named arguments".
In addition to the `sse` argument, the following named arguments can be used:
* `channel`:
The channel name used for the subscription. If missing, an empty string is assumed to be the channel name.
// type:
fio_str_info_s channel;
* `on_message`:
An optional callback to be called when a pub/sub message is received.
If missing, Data is directly written to the HTTP connection.
// callback example:
void on_message(http_sse_s *sse, fio_str_info_s channel,
fio_str_info_s msg, void *udata);
* `on_unsubscribe`:
An optional callback for when a subscription is fully canceled (the subscription's `udata` can be freed).
If missing, Data is directly written to the HTTP connection.
// callback example:
void on_unsubscribe(void *udata);
* `match`:
A callback for pattern matching, as described in [`fio_subscribe`](https://facil.io/0.7.x/fio/#fio_subscribe).
Note that only the `FIO_MATCH_GLOB` matching function (or NULL for no pattern matching) is safe to use with the Redis extension.
// callback example:
int foo_bar_match_fn(fio_str_info_s pattern, fio_str_info_s channel);
// type:
fio_match_fn match;
* `udata`:
Opaque user data.
// type:
void *udata;
Returns a subscription ID on success and 0 on failure.
#### `http_sse_unsubscribe`
```c
void http_sse_unsubscribe(http_sse_s *sse, uintptr_t subscription);
```
Cancels a subscription and invalidates the subscription object.
### Writing to the EventSource (SSE) Connection
#### `http_sse_write`
```c
int http_sse_write(http_sse_s *sse, struct http_sse_write_args);
#define http_sse_write(sse, ...) \
http_sse_write((sse), (struct http_sse_write_args){__VA_ARGS__})
```
Writes data to an EventSource (SSE) connection.
The `http_sse_write` function is shadowed by the `http_sse_write` MACRO, which allows the function to accept "named arguments".
In addition to the `sse` argument, the following named arguments can be used:
* `id`:
Sets the `id` event property (optional).
// type:
fio_str_info_s id;
* `event`:
Sets the `event` event property (optional).
// type:
fio_str_info_s event;
* `data`:
Sets the `data` event property (optional).
// type:
fio_str_info_s data;
* `retry`:
Sets the `retry` event property (optional).
// type:
fio_str_info_s retry;
Event field details can be found on the [Mozilla developer website](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events).
### EventSource (SSE) Helpers
#### `http_sse2uuid`
```c
intptr_t http_sse2uuid(http_sse_s *sse);
```
Get the connection's UUID (for `fio_defer_io_task`, pub/sub, etc').
#### `http_sse_dup`
```c
http_sse_s *http_sse_dup(http_sse_s *sse);
```
Duplicates an SSE handle by reference, remember to http_sse_free.
Returns the same object (increases a reference count, no allocation is made).
#### `http_sse_free`
```c
void http_sse_free(http_sse_s *sse);
```
Frees an SSE handle by reference (decreases the reference count).
## Miscellaneous
### Mime-Type helpers
The HTTP extension allows for easy conversion between file extensions and known Mime-Types.
Many known file extensions are registered by the HTTP extension during startup. However, it's also possible to add/register more Mime-Types during the setup stage.
NOTE:
The Mime-Type helpers are designed to allow for concurrent read access. By design, they are **not** thread safe.
It is recommended (and assumed) that all the calls to `http_mimetype_register` are performed during the setup stage (before calling `fio_start`).
#### `http_mimetype_register`
```c
void http_mimetype_register(char *file_ext, size_t file_ext_len,
FIOBJ mime_type_str);
```
Registers a Mime-Type to be associated with a file extension, taking ownership of the `mime_type_str` String (use `fiobj_dup` to keep a copy).
File extension names should exclude the dot (`'.'`) marking the beginning of the extension. i.e., use `"jpg"`, `"html"`, etc' (**not** `".jpg"`).
#### `http_mimetype_find`
```c
FIOBJ http_mimetype_find(char *file_ext, size_t file_ext_len);
```
Finds the mime-type associated with the file extension, returning a String on success and FIOBJ_INVALID on failure.
Remember to call `fiobj_free`.
#### `http_mimetype_find2`
```c
FIOBJ http_mimetype_find2(FIOBJ url);
```
Returns the mime-type associated with the URL or the default mime-type for
HTTP.
Remember to call `fiobj_free`.
#### `http_mimetype_clear`
```c
void http_mimetype_clear(void);
```
Clears the Mime-Type registry (it will be empty after this call).
### Time / Date Helpers
#### `http_gmtime`
```c
struct tm *http_gmtime(time_t timer, struct tm *tmbuf);
```
A faster (yet less localized) alternative to `gmtime_r`.
See the libc `gmtime_r` documentation for details.
Falls back to `gmtime_r` for dates before epoch.
### `http_date2rfc7231`
```c
size_t http_date2rfc7231(char *target, struct tm *tmbuf);
```
Writes an RFC 7231 date representation (HTTP date format) to the `target` buffer.
This requires ~32 bytes of space to be available at the target buffer (unless
it's a super funky year, 32 bytes is about 3 more than you need).
Returns the number of bytes actually written.
#### `http_date2str`
Alias for [`http_date2rfc7231`](#http_date2rfc7231).
#### `http_date2rfc2109`
```c
size_t http_date2rfc2109(char *target, struct tm *tmbuf);
```
Writes an RFC 2109 date representation to the `target` buffer. See [`http_date2rfc7231`](#http_date2rfc7231).
#### `http_date2rfc2822`
```c
size_t http_date2rfc2822(char *target, struct tm *tmbuf);
```
Writes an RFC 2822 date representation to the `target` buffer. See [`http_date2rfc7231`](#http_date2rfc7231).
#### `http_time2str`
```c
size_t http_time2str(char *target, const time_t t);
```
Prints Unix time to a HTTP time formatted string.
This variation implements cached results for faster processing, at the price of a less accurate string.
### URL String Decoding
#### `http_decode_url_unsafe`
```c
ssize_t http_decode_url_unsafe(char *dest, const char *url_data);
```
Decodes a URL encoded string, **no** buffer overflow protection.
#### `http_decode_url`
```c
ssize_t http_decode_url(char *dest, const char *url_data, size_t length);
```
Decodes a URL encoded string (query / form data), **no** buffer overflow protection.
#### `http_decode_path_unsafe`
```c
ssize_t http_decode_path_unsafe(char *dest, const char *url_data);
```
Decodes the "path" part of a request, **no** buffer overflow protection.
#### `http_decode_path`
```c
ssize_t http_decode_path(char *dest, const char *url_data, size_t length);
```
Decodes the "path" part of an HTTP request, no buffer overflow protection.
### Commonly Used Header Constants
Some headers are so commonly used, that the HTTP extension pre-allocates memory and objects to represent these headers.
Avoid freeing these headers, as the HTTP extension expects them to remain allocated until the application quits.
#### `HTTP_HEADER_ACCEPT`
```c
extern FIOBJ HTTP_HEADER_ACCEPT;
```
Represents the HTTP Header `"Accept"`.
#### `HTTP_HEADER_CACHE_CONTROL`
```c
extern FIOBJ HTTP_HEADER_CACHE_CONTROL;
```
Represents the HTTP Header `"Cache-Control"`.
#### `HTTP_HEADER_CONNECTION`
```c
extern FIOBJ HTTP_HEADER_CONNECTION;
```
Represents the HTTP Header `"Connection"`.
#### `HTTP_HEADER_CONTENT_ENCODING`
```c
extern FIOBJ HTTP_HEADER_CONTENT_ENCODING;
```
Represents the HTTP Header `"Content-Encoding"`.
#### `HTTP_HEADER_CONTENT_LENGTH`
```c
extern FIOBJ HTTP_HEADER_CONTENT_LENGTH;
```
Represents the HTTP Header `"Content-Length"`.
#### `HTTP_HEADER_CONTENT_RANGE`
```c
extern FIOBJ HTTP_HEADER_CONTENT_RANGE;
```
Represents the HTTP Header `"Content-Range"`.
#### `HTTP_HEADER_CONTENT_TYPE`
```c
extern FIOBJ HTTP_HEADER_CONTENT_TYPE;
```
Represents the HTTP Header `"Content-Type"`.
#### `HTTP_HEADER_COOKIE`
```c
extern FIOBJ HTTP_HEADER_COOKIE;
```
Represents the HTTP Header `"Cookie"`.
#### `HTTP_HEADER_DATE`
```c
extern FIOBJ HTTP_HEADER_DATE;
```
Represents the HTTP Header `"Date"`.
#### `HTTP_HEADER_ETAG`
```c
extern FIOBJ HTTP_HEADER_ETAG;
```
Represents the HTTP Header `"Etag"`.
#### `HTTP_HEADER_HOST`
```c
extern FIOBJ HTTP_HEADER_HOST;
```
Represents the HTTP Header `"Host"`.
#### `HTTP_HEADER_LAST_MODIFIED`
```c
extern FIOBJ HTTP_HEADER_LAST_MODIFIED;
```
Represents the HTTP Header `"Last-Modified"`.
#### `HTTP_HEADER_ORIGIN`
```c
extern FIOBJ HTTP_HEADER_ORIGIN;
```
Represents the HTTP Header `"Origin"`.
#### `HTTP_HEADER_SET_COOKIE`
```c
extern FIOBJ HTTP_HEADER_SET_COOKIE;
```
Represents the HTTP Header `"Set-Cookie"`.
#### `HTTP_HEADER_UPGRADE`
```c
extern FIOBJ HTTP_HEADER_UPGRADE;
```
Represents the HTTP Header `"Upgrade"`.
### Compile Time Settings
#### `HTTP_BUSY_UNLESS_HAS_FDS`
```c
#define HTTP_BUSY_UNLESS_HAS_FDS 64
```
When a new connection is accepted, it will be immediately declined with a 503 service unavailable (server busy) response unless the following number of file descriptors is available.
#### `HTTP_DEFAULT_BODY_LIMIT`
```c
#define HTTP_DEFAULT_BODY_LIMIT (1024 * 1024 * 50)
```
The default limit on HTTP message length (in bytes). A different limit can be set during runtime as part of the `http_listen` function call.
#### `HTTP_MAX_HEADER_COUNT`
```c
#define HTTP_MAX_HEADER_COUNT 128
```
The maximum (hard coded) number of headers per HTTP request, after which the request is considered malicious and the connection is abruptly closed.
#### `HTTP_MAX_HEADER_LENGTH`
```c
#define HTTP_MAX_HEADER_LENGTH 8192
```
the default maximum length for a single header line
# The Redis Extension
facil.io includes a Redis Pub/Sub extension that makes it easy to scale pub/sub applications horizontally.
The extension was written to minimize Redis connections and load, allowing more clusters to connect to a Redis server.
This requires that each facil.io cluster consume less resources - only two connections per cluster instead of two connections per process (as might be common on other implementations).
To use the facil.io Redis extension API, include the file `redis_engine.h`
## Connecting facil.io to Redis
By using the [Core Library's External Pub/Sub Services API](https://facil.io/0.7.x/fio/#external-pub-sub-services), it's easy to connect an application to a Redis Server. i.e.:
```c
fio_pubsub_engine_s *r = redis_engine_create(.address.data = "localhost");
if (!r){
perror("Couldn't initialize Redis");
exit(-1);
}
fio_state_callback_add(FIO_CALL_AT_EXIT,
(void (*)(void *))redis_engine_destroy, r);
FIO_PUBSUB_DEFAULT = r;
```
### Connection Management
#### `redis_engine_create`
```c
fio_pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
#define redis_engine_create(...) \
redis_engine_create((struct redis_engine_create_args){__VA_ARGS__})
```
Creates and attaches a Redis "engine", which connects to an external Redis service and manages Pub/Sub communication with that service.
The `redis_engine_create` function is shadowed by the `redis_engine_create` MACRO, which allows the function to accept "named arguments", as shown in the above example.
Th possible named arguments for the `redis_engine_create` function call are:
* `address`
Redis server's address, defaults to `"localhost"`.
fio_str_info_s address;
* `port`
Redis server's port, defaults to `"6379"`.
fio_str_info_s port;
* `auth`
Redis server's password, if any. */
fio_str_info_s auth;
* `ping_interval`
A `ping` will be sent every `ping_interval` interval or inactivity. */
uint8_t ping_interval;
The fio_fio_pubsub_engine_s is active only after facil.io starts running.
A `ping` will be sent every `ping_interval` interval or inactivity. The default value (0) will fallback to facil.io's maximum time of inactivity (5 minutes) before polling on the connection's protocol.
**Note**: The Redis engine can only be initialized *before* facil.io starts up, during the setup stage within the root process. Attempting to initialize a Redis engine while the application is running might not work (and requires a hot restart for any child processes).
#### `redis_engine_destroy`
```c
void redis_engine_destroy(fio_pubsub_engine_s *engine);
```
Detaches and destroys a Redis Pub/Sub engine from facil.io.
### Sending Redis Database Commands
#### `redis_engine_send`
```c
intptr_t redis_engine_send(fio_pubsub_engine_s *engine,
FIOBJ command,
void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply,
void *udata),
void *udata);
```
Sends a Redis command through the engine's connection.
The response will be sent back using the optional callback. `udata` is passed along untouched.
The message will be resent on network failures, until a response validates the fact that the command was sent (or the engine is destroyed).
**Note**: Avoid calling Pub/Sub commands using this function, as it could violate the Redis connection's protocol and could prevent the communication from resuming.
**Note2**: The Redis extension is designed for resource conservation, not speed. This might not be the best way to use Redis as a database and should be considered available for occasional use rather than heavy use.
### The RESP parser
The Redis extension includes a RESP parser authored as a single file library (`resp_parser.h`), which can be used independently from the rest of facil.io.
The parser was originally coded in order keep the licensing scheme simple and avoid a debate about MIT licensing vs. BSD-3-clause requirements.
#### `resp_parse`
```c
static size_t resp_parse(resp_parser_s *parser,
const void *buffer,
size_t length);
```
Parses a RESP content stream, invoking any RESP related callbacks as required.
The `resp_parse` will review the data and call any of the following callback, depending on the RESP protocol's state:
* `resp_on_message` - a local static callback, called when the RESP message was completely parsed.
static int resp_on_message(resp_parser_s *parser);
* `resp_on_number` - a local static callback, called when a Number object is parsed.
static int resp_on_number(resp_parser_s *parser, int64_t num);
* `resp_on_okay` - a local static callback, called when a OK message is received.
static int resp_on_okay(resp_parser_s *parser);
* `resp_on_null` - a local static callback, called when NULL is received.
static int resp_on_null(resp_parser_s *parser);
* `resp_on_start_string` - a local static callback, called when a String should be allocated.
`str_len` is the expected number of bytes that will fill the final string object, without any NUL byte marker (the string might be binary).
If this function returns any value besides 0, parsing is stopped.
static int resp_on_start_string(resp_parser_s *parser, size_t str_len);
* `resp_on_string_chunk` - a local static callback, called as String objects are streamed.
static int resp_on_string_chunk(resp_parser_s *parser, void *data, size_t len);
* `resp_on_end_string` - a local static callback, called when a String object had finished streaming.
static int resp_on_end_string(resp_parser_s *parser);
* `resp_on_err_msg` - a local static callback, called an error message is received.
static int resp_on_err_msg(resp_parser_s *parser, void *data, size_t len);
* a local static callback, called when an Array should be allocated.
`array_len` is the expected number of objects that will fill the Array object.
There's no `resp_on_end_array` callback since the RESP protocol, simply count back from `array_len`.
However, be aware that a client/server might send nested Arrays in some rare cases.
If this function returns any value besides 0, parsing is stopped.
static int resp_on_start_array(resp_parser_s *parser, size_t array_len);
* `resp_on_parser_error` - a local static callback, called when a parser / protocol error occurs.
static int resp_on_parser_error(resp_parser_s *parser);
Returns the number of bytes to be resent. i.e., for a return value 5, the last 5 bytes in the buffer need to be resent to the parser.
Data consumed can be safely overwritten (assuming it isn't used by the parsing implementation).
**NOTE**:
The `resp_parser_s` type should be considered opaque, without any user related data.
To attach data to the parser, include the parser within a container `struct`, i.e.:
```c
typedef struct {
/* place parser at top for pointer casting simplicity*/
resp_parser_s parser;
/* place user data after the parser */
void * udata;
} my_parser_s;
```
# Command Line Helper
This is a simple Command Line Interface helper extension that makes it easy to handle command line arguments.
To use the facil.io CLI API, include the file `fio_cli.h`
## Overview
Initializing the CLI helper is easy with a single call to the function/macro `fio_cli_start`.
Data can be accessed either as a C string or an integer using the `fio_cli_get` and `fio_cli_set` functions.
### example
```c
fio_cli_start(argc, argv, 0, 0, "this example accepts the following:",
FIO_CLI_PRINT_HREADER("Concurrency:"),
FIO_CLI_INT("-t -thread number of threads to run."),
FIO_CLI_INT("-w -workers number of workers to run."),
FIO_CLI_PRINT_HREADER("Address Binding:"),
FIO_CLI_STRING("-b, -address the address to bind to."),
FIO_CLI_INT("-p,-port the port to bind to."),
FIO_CLI_PRINT("\t\tset port to zero (0) for Unix s."),
FIO_CLI_PRINT_HREADER("Logging:"),
FIO_CLI_BOOL("-v -log enable logging."));
if (fio_cli_get_bool("-v")) {
printf("Logging is enabled\n");
}
fio_cli_end();
```
## Constants
The following constants are defined by the CLI extension.
#### `FIO_CLI_STRING(argument)`
Indicates the CLI argument should be a String (default).
#### `FIO_CLI_BOOL(argument)`
Indicates the CLI argument is a Boolean value.
#### `FIO_CLI_INT(argument)`
Indicates the CLI argument should be an Integer (numerical).
#### `FIO_CLI_PRINT_HEADER(string)`
Indicates the CLI argument should only be used when printing the help output.
Text will be printed underlined, in a new line, with an extra line break preceding it.
#### `FIO_CLI_PRINT(string)`
Indicates the CLI argument should only be used when printing the help output.
Text will be printed as normal text in a new line.
## Functions
### Initialization / Destruction
#### `fio_cli_start`
```c
void fio_cli_start(int argc, char const *argv[], int unnamed_min,
int unnamed_max, char const *description,
char const **names);
/* Automatically appends a NULL marker at the end of the `names` array. */
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
fio_cli_start((argc), (argv), (unnamed_min), (unnamed_max), (description), \
(char const *[]){__VA_ARGS__, NULL})
```
This function parses the Command Line Interface (CLI), creating a temporary "dictionary" that allows easy access to the CLI using their names or aliases.
Command line arguments may be typed. If an optional type requirement is provided and the provided argument fails to match the required type, execution will end and an error message will be printed along with a short "help".
The following optional type requirements are:
* FIO_CLI_STRING - (default) string argument.
* FIO_CLI_BOOL - boolean argument (no value).
* FIO_CLI_PRINT_HEADER - header text.
* FIO_CLI_PRINT - normal text.
Argument names MUST start with the '-' character. The first word starting
without the '-' character will begin the description for the CLI argument.
The arguments "-?", "-h", "-help" and "--help" are automatically handled
unless overridden.
Example use:
```c
fio_cli_start(argc, argv, 0, 0, "this example accepts the following:",
FIO_CLI_PRINT_HREADER("Concurrency:"),
FIO_CLI_INT("-t -thread number of threads to run."),
FIO_CLI_INT("-w -workers number of workers to run."),
FIO_CLI_PRINT_HREADER("Address Binding:"),
FIO_CLI_STRING("-b, -address the address to bind to."),
FIO_CLI_INT("-p,-port the port to bind to."),
FIO_CLI_PRINT("\t\tset port to zero (0) for Unix s."),
FIO_CLI_PRINT_HREADER("Logging:"),
FIO_CLI_BOOL("-v -log enable logging."));
```
This would allow access to the named arguments:
```c
fio_cli_get("-b") == fio_cli_get("-address");
```
Once all the data was accessed, free the parsed data dictionary using:
```c
fio_cli_end();
```
It should be noted, arguments will be recognized in a number of forms, i.e.:
```bash
app -t=1 -p3000 -a localhost
```
#### `fio_cli_end`
```c
void fio_cli_end(void);
```
Clears the memory used by the CLI dictionary, removing all parsed data.
### Access CLI arguments
#### `fio_cli_get`
```c
char const *fio_cli_get(char const *name);
```
Returns the argument's value as a NUL terminated C String.
#### `fio_cli_get_i`
```c
int fio_cli_get_i(char const *name);
```
Returns the argument's value as an integer.
#### `fio_cli_get_bool`
```c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
```
This MACRO returns the argument's value as a boolean.
#### `fio_cli_unnamed_count`
```c
unsigned int fio_cli_unnamed_count(void);
```
Returns the number of unnamed arguments.
Unnamed arguments are only relevant if the `unnamed_min` argument in `fio_cli_start` was greater than 0.
#### `fio_cli_unnamed`
```c
char const *fio_cli_unnamed(unsigned int index);
```
Returns the unnamed arguments using a 0 (zero) based `index`.
Unnamed arguments are only relevant if the `unnamed_min` argument in `fio_cli_start` was greater than 0.
### Set CLI argument data
#### `fio_cli_set`
```c
void fio_cli_set(char const *name, char const *value);
```
Sets the argument's value as a NUL terminated C String (no copy!).
CAREFUL: This does not automatically detect aliases or type violations! it will only effect the specific name given, even if invalid. i.e.:
```c
fio_cli_start(argc, argv, 0, 0,
"this is example accepts the following options:",
FIO_CLI_INT("-p -port the port to bind to"));
fio_cli_set("-p", "hello"); // fio_cli_get("-p") != fio_cli_get("-port");
```
Note: this does NOT copy the C strings to memory. Memory should be kept alive until `fio_cli_end` is called.
#### `fio_cli_set_default`
```c
#define fio_cli_set_default(name, value) \
if (!fio_cli_get((name))) \
fio_cli_set(name, value);
```
This MACRO sets the argument's value only if the argument has no existing value.
CAREFUL: This does not automatically detect aliases or type violations! it will only effect the specific name given, even if invalid. See [fio_cli_set](#fio_cli_set).
## Important Notes
The CLI extension is **NOT** thread safe. If you wish to write data to the CLI storage while facil.io is running, you should protect both read and write access to the storage.
# SSL/TLS Functions
SSL/TLS provides Transport Layer Security (TLS) for more secure communication.
facil.io attempts to make TLS connections easy by providing a simplified API that abstracts away the underlying TLS library.
Support for OpenSSL >= `1.1.0` is included starting with facil.io version 0.7.0.beta6.
Future support for BearSSL is planned and an example/template is provided under `fio_tls_missing.c`, making it easy to use your own SSL/TLS library.
The implementation leverages the [Read/Write Hooks](https://facil.io/0.7.x/fio/#lower-level-read-write-close-hooks) to allow TLS connections and unencrypted connections to share the same code-base and API (`fio_write`, `fio_read`, etc').
To use the facil.io TLS API, include the file `fio_tls.h`
### TLS Certificates and Settings
#### `fio_tls_new`
```c
fio_tls_s *fio_tls_new(const char *server_name,
const char *public_certificate_file,
const char *private_key_file,
const char *private_key_password);
```
Creates a new SSL/TLS context / settings object with a default certificate (if any).
```c
fio_tls_s * tls = fio_tls_new("www.example.com",
"./ssl/public_key.pem",
"./ssl/private_key.pem",
NULL);
```
If a server name is provided, than `NULL` values can be used to create an anonymous (unverified) context / settings object.
```c
fio_tls_s * tls = fio_tls_new("www.example.com", NULL, NULL, NULL);
```
If all values are `NULL`, a TLS object will be created without a certificate. This could be used for clients together with `fio_tls_trust`
```c
fio_tls_s * tls = fio_tls_new(NULL, NULL, NULL, NULL);
fio_tls_trust(tls, "google-ca.pem" );
```
`fio_tls_s *` is an opaque type used as a handle for the SSL/TLS functions. It shouldn't be directly accessed.
Remember to call `fio_tls_destroy` once the `fio_tls_s` object is no longer in use.
#### `fio_tls_dup`
```c
void fio_tls_dup(fio_tls_s *tls);
```
Increase the reference count for the TLS object.
Decrease / free with `fio_tls_destroy`.
#### `fio_tls_destroy`
```c
void fio_tls_destroy(fio_tls_s *tls);
```
Destroys the SSL/TLS context / settings object and frees any related resources / memory.
#### `fio_tls_cert_add`
```c
void fio_tls_cert_add(fio_tls_s *, const char *server_name,
const char *public_cert_file,
const char *private_key_file,
const char *private_key_password);
```
Adds a certificate a new SSL/TLS context / settings object (SNI support).
The `private_key_password` can be NULL if the private key PEM file isn't password protected.
```c
fio_tls_cert_add(tls, "www.example.com",
"./ssl/public_key.pem",
"./ssl/private_key.pem",
NULL);
```
#### `fio_tls_trust`
```c
void fio_tls_trust(fio_tls_s *, const char *public_cert_file);
```
Adds a certificate to the "trust" list, which automatically adds a peer verification requirement.
Note, when the fio_tls_s object is used for server connections, this will limit connections to clients that connect using a trusted certificate.
```c
fio_tls_trust(tls, "google-ca.pem" );
```
#### `fio_tls_alpn_add`
```c
void fio_tls_alpn_add(fio_tls_s *tls,
const char *protocol_name,
void (*on_selected)(intptr_t uuid,
void *udata_connection,
void *udata_tls),
void *udata_tls,
void (*on_cleanup)(void *udata_tls));
```
Adds an ALPN protocol callback to the SSL/TLS context.
The first protocol added will act as the default protocol to be selected.
The `on_selected` callback should accept the connection's `uuid`, the user data pointer passed to either `fio_tls_accept` or `fio_tls_connect` (here: `udata_connetcion`) and the user data pointer passed to the `fio_tls_alpn_add` function (`udata_tls`).
The `on_cleanup` callback will be called when the TLS object is destroyed (or `fio_tls_alpn_add` is called again with the same protocol name). The `udata_tls` argument will be passed along, as is, to the callback (if set).
Except for the `tls` and `protocol_name` arguments, all arguments can be NULL.
#### `fio_tls_alpn_count`
```c
uintptr_t fio_tls_alpn_count(fio_tls_s *tls);
```
Returns the number of registered ALPN protocol names.
This could be used when deciding if protocol selection should be delegated to the ALPN mechanism, or whether a protocol should be immediately assigned.
If no ALPN protocols are registered, zero (0) is returned.
### TLS Connection Establishment
#### `fio_tls_accept`
```c
void fio_tls_accept(intptr_t uuid, fio_tls_s *tls, void *udata);
```
Establishes an SSL/TLS connection as an SSL/TLS Server, using the specified context / settings object.
The `uuid` should be a socket UUID that is already connected to a peer (i.e., the result of `fio_accept`).
The `udata` is an opaque user data pointer that is passed along to the protocol selected (if any protocols were added using `fio_tls_alpn_add`).
#### `fio_tls_connect`
```c
void fio_tls_connect(intptr_t uuid, fio_tls_s *tls, void *udata);
```
Establishes an SSL/TLS connection as an SSL/TLS Client, using the specified context / settings object.
The `uuid` should be a socket UUID that is already connected to a peer (i.e., one received by a `fio_connect` specified callback `on_connect`).
The `udata` is an opaque user data pointer that is passed along to the protocol selected (if any protocols were added using `fio_tls_alpn_add`).
### TLS Compile-Time Options
#### `FIO_TLS_PRINT_SECRET`
```c
#ifndef FIO_TLS_PRINT_SECRET
/* if true, the master key secret should be printed using FIO_LOG_DEBUG */
#define FIO_TLS_PRINT_SECRET 0
#endif
```
By setting `FIO_TLS_PRINT_SECRET` to a true value (1), facil.io will compile in a way that prints out the master key / secret to the debugging log, for use with WireShark or similar network debugging tools.
# FIOBJ Generic Functions
Some `FIOBJ` functions can be called for any `FIOBJ` object, regardless of their type.
These generic functions are listed here.
### FIOBJ Memory Management
#### `fiobj_dup`
```c
FIOBJ fiobj_dup(FIOBJ);
```
Heuristic copy with a preference for copy by reference(!), to minimize allocations.
This usually means the only action performed is reference count increase.
Always returns the value passed along.
#### `fiobj_free`
```c
void fiobj_free(FIOBJ);
```
Frees the object and any of it's "children".
This function affects nested objects, meaning that when an Array or a Hash object is passed along, it's children (the nested objects) are also freed.
For Hash objects, only the value objects are deallocated. The `keys` aren't "owned" by the Hash Map and therefore they aren't automatically allocated.
#### `fiobj_send_free`
```c
ssize_t fiobj_send_free(intptr_t uuid, FIOBJ o) {
fio_str_info_s s = fiobj_obj2cstr(o);
return fio_write2(uuid, .data.buffer = (void *)(o),
.offset = (((intptr_t)s.data) - ((intptr_t)(o))),
.length = s.len, .after.dealloc = fiobj4sock_dealloc);
}
```
Sends a FIOBJ String object through a facil.io `uuid` socket and than frees the FIOBJ object.
This allows the same String object to be send multiple times to different clients by leveraging `fiobj_dup` and the FIOBJ's internal reference counting mechanism. i.e:
```c
/* for each client... */
fiobj_send_free(client_uuid, fiobj_dup(fiobj_string));
/* and than use fiobj_free, which will decrease the reference count. */
fiobj_free(fiobj_string);
```
**Note**: Using this function with FIOBJ objects that aren't a String (`FIOBJ_T_STRING`) might result in undefined behavior due to the way the String data is rendered by `fiobj_obj2cstr`.
### FIOBJ Soft Type Recognition
#### `fiobj_type`
```c
fiobj_type_enum fiobj_type(FIOBJ o);
```
Returns the object's type.
Valid return values are:
- `FIOBJ_T_NULL` - Object is the primitive `null`
- `FIOBJ_T_TRUE` - Object is the primitive `true`
- `FIOBJ_T_FALSE` - Object is the primitive `false`
- `FIOBJ_T_NUMBER` - Object is a number.
- `FIOBJ_T_FLOAT` - Object is a floating point number (`double`).
- `FIOBJ_T_STRING` - Object is a binary String.
- `FIOBJ_T_ARRAY` - Object is a FIOBJ array.
- `FIOBJ_T_HASH` - Object is a FIOBJ hash.
- `FIOBJ_T_DATA` - Object is a data stream, either wrapping a temporary file or a memory block.
- `FIOBJ_T_UNKNOWN` - Object type is unknown (a user's type).
#### `fiobj_type_is`
```c
size_t fiobj_type_is(FIOBJ o, fiobj_type_enum type)
```
This is faster than getting the type, since parts of the switch statement are optimized away (they are calculated during compile time).
#### `fiobj_type_name`
```c
const char *fiobj_type_name(const FIOBJ obj);
```
Returns a C string naming the objects dynamic type.
### Object Equality / Truthfulness
#### `fiobj_is_true`
```c
int fiobj_is_true(const FIOBJ);
```
Tests if an object evaluates as TRUE.
This is object type specific. For example, empty strings might evaluate as FALSE, even though they aren't a boolean type.
#### `fiobj_iseq`
```c
int fiobj_iseq(const FIOBJ obj1, const FIOBJ obj2);
```
Deeply compares two objects. No hashing or recursive function calls are
involved.
Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
Hash objects are order sensitive. To be equal, Hash keys must match in order.
Returns 1 if true and 0 if false.
### Object Conversion
#### `fiobj_obj2num`
```c
intptr_t fiobj_obj2num(const FIOBJ obj);
```
Returns an Object's numerical value.
If a String is passed to the function, it will be parsed assuming base 10
numerical data.
Hashes and Arrays return their object count.
IO objects return the length of their data.
A type error results in 0.
#### `fiobj_obj2float`
```c
double fiobj_obj2float(const FIOBJ obj);
```
Returns a Float's value.
If a String is passed to the function, they will benparsed assuming base 10
numerical data.
A type error results in 0.
#### `fiobj_obj2cstr`
```c
fio_str_info_s fiobj_obj2cstr(const FIOBJ obj);
```
Returns a C String (NUL terminated) using the `fio_str_info_s` data type:
```c
typedef struct fio_str_info_s {
size_t capa; /* String capacity, if the string is writable. (only FIOBJ_T_STRING) */
size_t len; /* String length. */
char *data; /* String's first byte. */
} fio_str_info_s;
```
The Sting in binary safe and might contain NUL bytes in the middle as well as
a terminating NUL.
If a Number or a Float is passed to the function, they
will be parsed as a *temporary*, thread-safe, String.
Numbers will be represented in base 10 numerical data.
A type error results in NULL (i.e. object can't be represented automatically as a String).
#### `fiobj_obj2hash`
```c
uint64_t fiobj_obj2hash(const FIOBJ o);
```
Calculates an Object's hash value for possible use as a HashMap key.
Hash values use the default hashing function defined at compile time. This is usually SipHash 1-3, but could be set to a different hashing function (such as RiskyHash).
The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In other words, Hash Objects and Arrays can NOT be used for Hash keys.
### Iteration
#### `fiobj_each1`
```c
size_t fiobj_each1(FIOBJ, size_t start_at,
int (*task)(FIOBJ obj, void *arg), void *arg);
```
Single layer iteration using a callback for each nested fio object.
Accepts any `FIOBJ` type but only collections (Arrays and Hashes) are
processed. The container itself (the Array or the Hash) is **not** processed
(unlike `fiobj_each2`).
The callback task function must accept an object and an opaque user pointer.
Hash objects pass along only the value object. The keys can be accessed using the [`fiobj_hash_key_in_loop`](#fiobj_hash_key_in_loop) function.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the "stop" position, i.e., the number of items processed + the
starting point.
#### `fiobj_each2`
```c
size_t fiobj_each2(FIOBJ, int (*task)(FIOBJ obj, void *arg), void *arg);
```
Deep iteration using a callback for each fio object, including the parent.
Accepts any `FIOBJ ` type.
Collections (Arrays, Hashes) are deeply probed and shouldn't be edited
during an `fiobj_each2` call (or weird things may happen).
The callback task function must accept an object and an opaque user pointer.
Hash objects keys are available using the [`fiobj_hash_key_in_loop`](#fiobj_hash_key_in_loop) function.
Notice that when passing collections to the function, the collection itself
is sent to the callback followed by it's children (if any). This is true also
for nested collections (a nested Hash will be sent first, followed by the
nested Hash's children and then followed by the rest of it's siblings.
If the callback returns -1, the loop is broken. Any other value is ignored.
#### `fiobj_hash_key_in_loop`
```c
FIOBJ fiobj_hash_key_in_loop(void);
```
Returns the key for the object in the current `fiobj_each` loop (if any).
# FIOBJ Primitives
The following Primitive types are defined:
#### `fiobj_null`
```c
FIOBJ fiobj_null(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
#### `fiobj_true`
```c
FIOBJ fiobj_true(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
#### `fiobj_false`
```c
FIOBJ fiobj_false(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
# FIOBJ Numbers API
### Numbers API (Integers)
#### `fiobj_num_new`
```c
FIOBJ fiobj_num_new(intptr_t num);
```
Creates a Number object. Remember to use `fiobj_free`.
#### `fiobj_num_tmp`
```c
FIOBJ fiobj_num_tmp(intptr_t num);
```
Creates a temporary Number object. Avoid using `fiobj_free`.
### Float API (Double)
#### `fiobj_float_new`
```c
FIOBJ fiobj_float_new(double num);
```
Creates a Float object. Remember to use `fiobj_free`.
#### `fiobj_float_set`
```c
void fiobj_float_set(FIOBJ obj, double num);
```
Mutates a Float object's value. Effects every object's reference!
#### `fiobj_float_tmp`
```c
FIOBJ fiobj_float_tmp(double num);
```
Creates a temporary Float object. Avoid using `fiobj_free`.
### Numerical Helpers: not FIOBJ specific, but included as part of the library
#### `fio_atol`
```c
int64_t fio_atol(char **pstr);
```
A helper function that converts between String data to a signed int64_t.
Numbers are assumed to be in base 10.
The `0x##` (or `x##`) and `0b##` (or `b##`) are recognized as base 16 and
base 2 (binary MSB first) respectively.
The pointer will be updated to point to the first byte after the number.
#### `fio_atof`
```c
double fio_atof(char **pstr);
```
A helper function that converts between String data to a signed double.
#### `fio_ltoa`
```c
size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
```
A helper function that converts between a signed int64_t to a string.
No overflow guard is provided, make sure there's at least 66 bytes available
(for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL terminator).
#### `fio_ftoa`
```c
size_t fio_ftoa(char *dest, double num, uint8_t base);
```
A helper function that converts between a double to a string.
No overflow guard is provided, make sure there's at least 130 bytes available
(for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL terminator).
#### `fio_ltocstr` (deprecated)
```c
fio_str_info_s fio_ltocstr(long);
```
Converts a number to a temporary, thread local, C string object
The `fio_str_s` object is only valid until the function is called again within the same thread or the thread terminates.
#### `fio_ftocstr` (deprecated)
```c
fio_str_info_s fio_ftocstr(double);
```
Converts a float to a temporary, thread local, C string object
The `fio_str_s` object is only valid until the function is called again within the same thread or the thread terminates.
# FIOBJ String API
See the [core FIOBJ functions](https://facil.io/0.7.x/fiobj_core/) for accessing the String data ([`fiobj_obj2cstr`](https://facil.io/0.7.x/fiobj_core/#fiobj_obj2cstr)) and freeing the String object ([`fiobj_free`](https://facil.io/0.7.x/fiobj_core/#fiobj_free)).
### FIOBJ String Initialization
#### `fiobj_str_new`
```c
FIOBJ fiobj_str_new(const char *str, size_t len);
```
Creates a String object. Remember to use `fiobj_free`.
#### `fiobj_str_buf`
```c
FIOBJ fiobj_str_buf(size_t capa);
```
Creates a String object with pre-allocation for Strings up to `capa` long.
If `capa` is zero, **a whole memory page** will be allocated.
Remember to use `fiobj_free`.
#### `fiobj_str_copy`
```c
static inline FIOBJ fiobj_str_copy(FIOBJ src);
```
Creates a copy from an existing String. Remember to use `fiobj_free`.
Implemented as:
```c
static inline FIOBJ fiobj_str_copy(FIOBJ src) {
fio_str_info_s s = fiobj_obj2cstr(src);
return fiobj_str_new(s.data, s.len);
}
```
#### `fiobj_str_move`
```c
FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity);
```
Creates a String object. Remember to use `fiobj_free`.
It's possible to wrap a previously allocated memory block in a FIOBJ String object, as long as it was allocated using `fio_malloc`.
The ownership of the memory indicated by `str` will "move" to the object and will be freed (using `fio_free`) once the object's reference count drops to zero.
Note: The original memory MUST be allocated using `fio_malloc` (NOT the system's `malloc`) and it will be freed using `fio_free`.
#### `fiobj_str_tmp`
```c
FIOBJ fiobj_str_tmp(void);
```
Returns a thread-static temporary string. Avoid calling `fiobj_dup` or `fiobj_free`.
## String Manipulation and Data
#### `fiobj_str_freeze`
```c
void fiobj_str_freeze(FIOBJ str);
```
Prevents the String object from being changed.
When a String is used as a key for a Hash, it is automatically frozen to prevent the Hash from becoming broken.
#### `fiobj_str_capa_assert`
```c
size_t fiobj_str_capa_assert(FIOBJ str, size_t size);
```
Confirms the requested capacity is available and allocates as required.
Returns updated capacity.
#### `fiobj_str_capa`
```c
size_t fiobj_str_capa(FIOBJ str);
```
Returns a String's capacity, if any. This should include the NUL byte.
#### `fiobj_str_resize`
```c
void fiobj_str_resize(FIOBJ str, size_t size);
```
Resizes a String object, allocating more memory if required.
#### `fiobj_str_compact`
```c
void fiobj_str_compact(FIOBJ str);
```
Performs a best attempt at minimizing memory consumption.
Actual effects depend on the underlying memory allocator and it's implementation. Not all allocators will free any memory.
#### `fiobj_str_compact`
```c
#define fiobj_str_minimize(str) fiobj_str_compact((str))
```
Alias for `fiobj_str_compact`.
#### `fiobj_str_clear`
```c
void fiobj_str_clear(FIOBJ str);
```
Empties a String's data, but keeps the memory used for that data available.
#### `fiobj_str_write`
```c
size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len);
```
Writes data at the end of the string, resizing the string as required.
Returns the new length of the String.
#### `fiobj_str_write_i`
```c
size_t fiobj_str_write_i(FIOBJ dest, int64_t num)
```
Writes a number at the end of the String using normal base 10 notation.
Returns the new length of the String
#### `fiobj_str_printf`
```c
size_t fiobj_str_printf(FIOBJ dest, const char *format, ...);
```
Writes data at the end of the string using a `printf` like interface, resizing the string as required.
Returns the new length of the String
#### `fiobj_str_vprintf`
```c
size_t fiobj_str_vprintf(FIOBJ dest, const char *format, va_list argv);
```
Writes data at the end of the string using a `vprintf` like interface, resizing the string as required.
Returns the new length of the String.
#### `fiobj_str_concat`
```c
size_t fiobj_str_concat(FIOBJ dest, FIOBJ source);
```
Writes data at the end of the string, resizing the string as required.
Remember to call `fiobj_free` to free the source (when done with it).
Returns the new length of the String.
#### `fiobj_str_join`
```c
#define fiobj_str_join(dest, src) fiobj_str_concat((dest), (src))
```
Alias for [`fiobj_str_concat`](#fiobj_str_concat).
#### `fiobj_str_readfile`
```c
size_t fiobj_str_readfile(FIOBJ dest, const char *filename, intptr_t start_at,
intptr_t limit);
```
Dumps the contents of `filename` at the end of the String.
If `limit == 0`, than the data will be read until EOF.
If the file can't be located, opened or read, or if `start_at` is out of bounds (i.e., beyond the EOF position), FIOBJ_INVALID is returned.
If `start_at` is negative, it will be computed from the end of the file.
Remember to use `fiobj_free`.
NOTE: Requires a POSIX system, or might silently fail.
### Hashing
#### `fiobj_str_hash`
```c
uint64_t fiobj_str_hash(FIOBJ o);
```
Calculates a String's hash value for possible use as a Hash Map key.
Hash values use the default hashing function defined at compile time. This is usually SipHash 1-3, but could be set to a different hashing function (such as RiskyHash).
**Note**:
Since FIOBJ hashes are impervious to [hash flooding attacks](https://medium.freecodecamp.org/hash-table-attack-8e4371fc5261) by design and don't require a strong hashing function. However, objects often contain network (external) information, and it would be better to use a strong hashing function.
# FIOBJ Array API
### Array creation API
#### `fiobj_ary_new`
```c
FIOBJ fiobj_ary_new(void);
```
Creates a mutable empty Array object. Use `fiobj_free` when done.
#### `fiobj_ary_new2`
```c
FIOBJ fiobj_ary_new2(size_t capa);
```
Creates a mutable empty Array object with the requested capacity.
### Accessing Array Members / Data
#### `fiobj_ary_count`
```c
size_t fiobj_ary_count(FIOBJ ary);
```
Returns the number of elements in the Array.
#### `fiobj_ary_capa`
```c
size_t fiobj_ary_capa(FIOBJ ary);
```
Returns the current, temporary, array capacity (it's dynamic).
#### `fiobj_ary2ptr`
```c
FIOBJ *fiobj_ary2ptr(FIOBJ ary);
```
Returns a TEMPORARY pointer to the beginning of the array.
This pointer can be used for sorting and other direct access operations as long as no other actions (insertion/deletion) are performed on the array.
#### `fiobj_ary_index`
```c
FIOBJ fiobj_ary_index(FIOBJ ary, int64_t pos);
```
Returns a temporary object owned by the Array.
Wrap this function call within `fiobj_dup` to get a persistent handle. i.e.:
```c
fiobj_dup(fiobj_ary_index(array, 0));
```
Negative values are retrieved from the end of the array. i.e., `-1` is the last item.
/** */
/**
Sets an object at the requested position.
/
void fiobj_ary_set(FIOBJ ary, FIOBJ obj, int64_t pos);
#### `fiobj_ary_entry`
```c
#define fiobj_ary_entry(a, p) fiobj_ary_index((a), (p))
```
Alias for [`fiobj_ary_index`](#fiobj_ary_index).
### Array push / shift API
#### `fiobj_ary_push`
```c
void fiobj_ary_push(FIOBJ ary, FIOBJ obj);
```
Pushes an object to the end of the Array.
#### `fiobj_ary_pop`
```c
FIOBJ fiobj_ary_pop(FIOBJ ary);
```
Pops an object from the end of the Array.
#### `fiobj_ary_unshift`
```c
void fiobj_ary_unshift(FIOBJ ary, FIOBJ obj);
```
Unshifts an object to the beginning of the Array. This could be expensive.
#### `fiobj_ary_shift`
```c
FIOBJ fiobj_ary_shift(FIOBJ ary);
```
Shifts an object from the beginning of the Array.
# Find / Remove / Replace
#### `fiobj_ary_replace`
```c
FIOBJ fiobj_ary_replace(FIOBJ ary, FIOBJ obj, int64_t pos);
```
Replaces the object at a specific position, returning the old object -
remember to `fiobj_free` the old object.
#### `fiobj_ary_find`
```c
int64_t fiobj_ary_find(FIOBJ ary, FIOBJ data);
```
Finds the index of a specified object (if any). Returns -1 if the object
isn't found.
#### `fiobj_ary_remove`
```c
int fiobj_ary_remove(FIOBJ ary, int64_t pos);
```
Removes the object at the index (if valid), changing the index of any
following objects.
Returns 0 on success or -1 (if no object or out of bounds).
#### `fiobj_ary_remove2`
```c
int fiobj_ary_remove2(FIOBJ ary, FIOBJ data);
```
Removes the first instance of an object from the Array (if any), changing the
index of any following objects.
Returns 0 on success or -1 (if the object wasn't found).
### Array Compacting
#### `fiobj_ary_compact`
```c
void fiobj_ary_compact(FIOBJ ary);
```
Removes any NULL *pointers* from an Array, keeping all Objects (including
explicit NULL objects) in the array.
This action is O(n) where n in the length of the array.
It could get expensive.
# FIOBJ Hash Map API
### Hash Creation
#### `fiobj_hash_new`
```c
FIOBJ fiobj_hash_new(void);
```
Creates a mutable empty Hash object. Use `fiobj_free` when done.
Notice that these Hash objects are optimized for smaller collections and retain order of object insertion.
#### `fiobj_hash_new2`
```c
FIOBJ fiobj_hash_new2(size_t capa);
```
Creates a mutable empty Hash object with an initial capacity of `capa`. Use `fiobj_free` when done.
This allows optimizations for larger (or smaller) collections.
### Hash properties and state
#### `fiobj_hash_capa`
```c
size_t fiobj_hash_capa(const FIOBJ hash);
```
Returns a temporary theoretical Hash map capacity. This could be used for testing performance and memory consumption.
#### `fiobj_hash_count`
```c
size_t fiobj_hash_count(const FIOBJ hash);
```
Returns the number of elements in the Hash.
### Populating and Managing the Hash
#### `fiobj_hash_set`
```c
int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj);
```
Sets a key-value pair in the Hash, duplicating the Symbol and **moving** the ownership of the object to the Hash.
Returns -1 on error.
#### `fiobj_hash_pop`
```c
FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key);
```
Allows the Hash to be used as a stack.
If a pointer `key` is provided, it will receive ownership of the key (remember to free).
Returns FIOBJ_INVALID on error.
Returns and object if successful (remember to free).
#### `fiobj_hash_replace`
```c
FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj);
```
Replaces the value in a key-value pair, returning the old value (and it's ownership) to the caller.
A return value of FIOBJ_INVALID indicates that no previous object existed (but a new key-value pair was created.
Errors are silently ignored.
Remember to free the returned object.
#### `fiobj_hash_remove`
```c
FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key);
```
Removes a key-value pair from the Hash, if it exists, returning the old object (instead of freeing it).
#### `fiobj_hash_remove2`
```c
FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t key_hash);
```
Removes a key-value pair from the Hash, if it exists, returning the old object (instead of freeing it).
#### `fiobj_hash_delete`
```c
int fiobj_hash_delete(FIOBJ hash, FIOBJ key);
```
Deletes a key-value pair from the Hash, if it exists, freeing the associated object.
Returns -1 on type error or if the object never existed.
#### `fiobj_hash_delete2`
```c
int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash);
```
Deletes a key-value pair from the Hash, if it exists, freeing the associated object.
This function takes a `uint64_t` Hash value (see `fio_siphash`) to perform a lookup in the HashMap, which is slightly faster than the other variations.
Returns -1 on type error or if the object never existed.
#### `fiobj_hash_get`
```c
FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key);
```
Returns a temporary handle to the object associated with the Symbol, FIOBJ_INVALID if none.
#### `fiobj_hash_get2`
```c
FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash);
```
Returns a temporary handle to the object associated hashed key value.
This function takes a `uint64_t` Hash value (see `fio_siphash`) to
perform a lookup in the HashMap, which is slightly faster than the other
variations.
Returns FIOBJ_INVALID if no object is associated with this hashed key value.
#### `fiobj_hash_haskey`
```c
int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key);
```
Returns 1 if the key (Symbol) exists in the Hash, even if it's value is NULL.
#### `fiobj_hash_clear`
```c
void fiobj_hash_clear(const FIOBJ hash);
```
Empties the Hash.
### Rehashing
Rehashing is usually performed automatically, as needed, and shouldn't be performed manually unless there is a known reason to do so.
#### `fiobj_hash_rehash`
```c
void fiobj_hash_rehash(FIOBJ h);
```
Attempts to rehash the Hash Map.
# The FIOBJ Data Stream Type
### Creating the Data Stream object
#### `fiobj_data_newstr`
```c
FIOBJ fiobj_data_newstr(void);
```
Creates a new local in-memory Data Stream object.
#### `fiobj_data_newstr2`
```c
FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
void (*dealloc)(void *));
```
Creates a Data object from an existing buffer.
The buffer will be deallocated using the provided `dealloc` function pointer.
Use a NULL `dealloc` function pointer if the buffer is static and shouldn't be freed.
#### `fiobj_data_newtmpfile`
```c
FIOBJ fiobj_data_newtmpfile(void);
```
Creates a new local tempfile Data Stream object.
#### `fiobj_data_newfd`
```c
FIOBJ fiobj_data_newfd(int fd);
```
Creates a new local file Data Stream object.
#### `fiobj_data_slice`
```c
FIOBJ fiobj_data_slice(FIOBJ parent, intptr_t offset, uintptr_t length);
```
Creates a slice from an existing Data Stream object.
### Saving the Data Stream object
#### `fiobj_data_save`
```c
int fiobj_data_save(FIOBJ io, const char *filename);
```
Creates a new local file Data Stream object.
### Reading Data From The Stream
#### `fiobj_data_read`
```c
fio_str_info_s fiobj_data_read(FIOBJ io, intptr_t length);
```
Reads up to `length` bytes and returns a temporary(!) buffer object (not NUL terminated).
If `length` is zero or negative, it will be computed from the end of the input backwards (0 == EOF).
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### `fiobj_data_read2ch`
```c
fio_str_info_s fiobj_data_read2ch(FIOBJ io, uint8_t token);
```
Reads until the `token` byte is encountered or until the end of the stream.
Returns a temporary(!) C string including the end of line marker.
Careful when using this call on large file streams, as the whole file stream might be loaded into the memory.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### ``
```c
#define fiobj_data_gets(io) fiobj_data_read2ch((io), '\n');
```
Reads a line (until the '\n' byte is encountered) or until the end of the available data.
Returns a temporary(!) buffer object (not NUL terminated) including the end of line marker.
Careful when using this call on large file streams, as the whole file stream might be loaded into the memory.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### `fiobj_data_pos`
```c
intptr_t fiobj_data_pos(FIOBJ io);
```
Returns the current reading position. Returns -1 on error.
#### `fiobj_data_len`
```c
intptr_t fiobj_data_len(FIOBJ io);
```
Returns the length of the stream.
#### `fiobj_data_seek`
```c
void fiobj_data_seek(FIOBJ io, intptr_t position);
```
Moves the reading position to the requested position.
#### `fiobj_data_pread`
```c
fio_str_info_s fiobj_data_pread(FIOBJ io, intptr_t start_at, uintptr_t length);
```
Reads up to `length` bytes starting at `start_at` position and returns a temporary(!) buffer object (not NUL terminated) string object. The reading position is ignored and unchanged.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
### Writing To The Data Stream
#### `fiobj_data_write`
```c
intptr_t fiobj_data_write(FIOBJ io, void *buffer, uintptr_t length);
```
Writes `length` bytes at the end of the Data Stream stream, ignoring the reading position.
Behaves and returns the same value as the system call `write`.
#### `fiobj_data_puts`
```c
intptr_t fiobj_data_puts(FIOBJ io, void *buffer, uintptr_t length);
```
Writes `length` bytes at the end of the Data Stream stream, ignoring the reading position, adding an EOL marker ("\r\n") to the end of the stream.
Behaves and returns the same value as the system call `write`.
#### `fiobj_data_assert_dynamic`
```c
void fiobj_data_assert_dynamic(FIOBJ io);
```
Makes sure the Data Stream object isn't attached to a static or external string.
If the Data Stream object is attached to a static or external string, the data will be copied to a new memory block.
If the Data Stream object is a slice from another Data Stream object, the data will be copied and the type of Data Stream object (memory vs. tmpfile) will be inherited.
# JSON API
## Overview
Parsing, editing and outputting JSON in C can be easily accomplished using [facil.io's dynamic types](https://facil.io/0.7.x/fiobj/) (`FIOBJ`).
There are [faster alternatives as well as slower alternatives out there](json_performance.html) (i.e., the Qajson4c library is probably the most balanced alternative).
However, `facil.io` offers the added benefit of complete parsing from JSON to object. This is in contrast to some parsers that offer a mid-way structure (often a linked list of JSON nodes) or lazy (delayed) parsing for types such as `true`, `false` and Numbers.
`facil.io` also offers the added benefit of complete formatting from a framework wide object type (`FIOBJ`) to JSON. This is in contrast to some solutions that require a linked list of node structures.
### Example
The `facil.io` parser will parse any C string until it either consumes the whole string or completes parsing of a JSON object.
For example, the following program will minify (or prettify) JSON data:
```c
// include the `fiobj` module from `facil.io`
#include "fiobj.h"
#include
// this is passed as an argument to `fiobj_obj2json`
// change this to 1 to prettify.
#define PRETTY 1
int main(int argc, char const *argv[]) {
// a default string to demo
const char *json = u8"{\n\t\"id\":1,\n"
"\t// comments are ignored.\n"
"\t\"number\":42,\n"
"\t\"float\":42.42,\n"
"\t\"string\":\"\\uD834\\uDD1E oh yeah...\",\n"
"\t\"hash\":{\n"
"\t\t\"nested\":true\n"
"\t},\n"
"\t\"symbols\":[\"id\","
"\"number\",\"float\",\"string\",\"hash\",\"symbols\"]\n}";
if (argc == 2) {
// accept command line JSON data
json = argv[1];
}
printf("\nattempting to parse:\n%s\n", json);
// Parsing the JSON
FIOBJ obj = FIOBJ_INVALID;
size_t consumed = fiobj_json2obj(&obj, json, strlen(json));
// test for errors
if (!consumed || !obj) {
printf("\nERROR, couldn't parse data.\n");
exit(-1);
}
// Example use - printing some JSON data
FIOBJ key = fiobj_str_new("number", 6);
if (FIOBJ_TYPE_IS(obj, FIOBJ_T_HASH) // make sure the JSON object is a Hash
&& fiobj_hash_get(obj, key)) { // test for the existence of "number"
printf("JSON print example - the meaning of life is %zu",
(size_t)fiobj_obj2num(fiobj_hash_get(obj, key)));
}
fiobj_free(key);
// Formatting the JSON back to a String object and printing it up
FIOBJ str = fiobj_obj2json(obj, PRETTY);
printf("\nParsed JSON input was: %zu bytes"
"\nJSON output is %zu bytes:\n\n%s\n\n",
consumed, (size_t)fiobj_obj2cstr(str).len, fiobj_obj2cstr(str).data);
// cleanup
fiobj_free(str);
fiobj_free(obj);
return 0;
}
```
## Constants
`JSON_MAX_DEPTH` is the maximum depth for nesting. The default value is 32 (should be set during compile time).
Since bit mapping is used, the maximum available nesting value is 32 (32 nested levels use 32 bits in a `uint32_t` type).
Note that facil.io avoids recursion to protect against DoS attacks that attempt stack exploding techniques.
## Types
### Parsing result types
The JSON parser returns a `FIOBJ` dynamic object of any type (depending on the JSON data).
No assumptions should be made
### Formating result type
The JSON formatter returns a `FIOBJ` dynamic String, always.
## Functions
### `fiobj_json2obj`
```c
size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
```
Parses JSON, setting `pobj` to point to the new Object.
Returns the number of bytes consumed. On Error, 0 is returned and no data is consumed.
### `fiobj_obj2json`
```c
FIOBJ fiobj_obj2json(FIOBJ, uint8_t pretty);
```
Stringify an object into a JSON string. Remember to `fiobj_free`.
Note that only the following basic fiobj types are supported: Primitives (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
Some objects (such as the POSIX specific IO type) are unsupported and may be formatted incorrectly.
### `fiobj_obj2json2`
```c
FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ object, uint8_t pretty);
```
Formats an object into a JSON string, appending the JSON string to an existing String. Remember to `fiobj_free` as usual.
Note that only the following basic fiobj types are supported: Primitives (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
Some objects (such as the POSIX specific IO type) are unsupported and may be formatted incorrectly.
## Important Notes
The parser assumes the whole JSON data is present in the data's buffer. A streaming parser is coded into the [`fiobj_json.c` source file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fiobj_json.c) but no external API is exposed.
The [`fiobj_json.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fiobj_json.h) might include more data.
# FIOBJ Mustache API
[Mustache logic-less template](http://mustache.github.io) rendering is supported using the `FIOBJ` types and the included mustache parser (which, much like the JSON parser, can be used independently).
Because both the JSON and the Mustache API are based on the FIOBJ type system, it's easy to render JSON data using a Mustache template.
### Mustache Template Initialization API
Template data can be loaded from a file or from memory.
By loading the data from a file, or by providing a file name when loading the template from memory, partial templates are automatically resolved, loaded and parsed.
#### `fiobj_mustache_load`
```c
mustache_s *fiobj_mustache_load(fio_str_info_s filename);
```
Loads a mustache template from a file, converting it into an opaque instruction array.
This is the same as calling:
```c
fiobj_mustache_new(.filename = filename.data, .filename_len = filename.len);
```
Returns an opaque `mustache_s` pointer to the instruction array or NULL (on error).
The `filename` argument should contain the template's file name.
Remember to call `fiobj_mustache_free` when done with the template.
#### `fiobj_mustache_new`
```c
mustache_s *fiobj_mustache_new(mustache_load_args_s args);
#define fiobj_mustache_new(...) \
fiobj_mustache_new((mustache_load_args_s){__VA_ARGS__})
```
Loads a mustache template, either from memory or a file, converting it into an opaque instruction array.
Returns an opaque `mustache_s` pointer to the instruction array or NULL (on error).
The `mustache_s *` object can be used to render the same template multiple times concurrently.
The `fiobj_mustache_new` function is shadowed by the `fiobj_mustache_new` MACRO, which allows the function to accept any of the following "named arguments":
* **`filename`** the root template's file name.
```c
// argument type:
char const *filename
```
* **`filename_len`** the file name's length.
```c
// argument type:
size_t filename_len
```
* **`data`** if both `data` and `data_len` are set, `data` will be used as the file's contents (instead of reading the file).
```c
// argument type:
char const *data
```
* **`data_len`** if set, `data` will be used as the file's contents.
```c
// argument type:
size_t data_len
```
* **`err`** a container for any template load errors.
```c
// argument type:
mustache_error_en *err
```
Remember to call `fiobj_mustache_free` when done with the template.
**Note**:
By setting the `filename` argument even when the `data` argument exists, it will allow path resolution for partial templates. Otherwise, there is no way to know where to find the partial templates.
The `mustache_error_en` type is an `enum` which, if set, will contain any of the following:
```c
typedef enum mustache_error_en {
MUSTACHE_OK,
MUSTACHE_ERR_TOO_DEEP,
MUSTACHE_ERR_CLOSURE_MISMATCH,
MUSTACHE_ERR_FILE_NOT_FOUND,
MUSTACHE_ERR_FILE_TOO_BIG,
MUSTACHE_ERR_FILE_NAME_TOO_LONG,
MUSTACHE_ERR_EMPTY_TEMPLATE,
MUSTACHE_ERR_UNKNOWN,
MUSTACHE_ERR_USER_ERROR,
} mustache_error_en;
```
#### `fiobj_mustache_free`
```c
void fiobj_mustache_free(mustache_s *mustache);
```
Frees the mustache template pointer immediately (careful when using the template concurrently).
### Mustache Template Rendering API
#### `fiobj_mustache_build`
```c
FIOBJ fiobj_mustache_build(mustache_s *mustache, FIOBJ data);
```
Creates a FIOBJ String containing the rendered template using the information in the FIOBJ `data` object (which should be a `FIOBJ_T_HASH` or data access might fail).
Returns FIOBJ_INVALID if an error occurred and a FIOBJ String on success.
Remember to call `fiobj_free` to free the String (or call `fiobj_send_free`).
**Note**: The `mustache_s *` object can be used to render the same template multiple times concurrently.
#### `fiobj_mustache_build2`
```c
FIOBJ fiobj_mustache_build2(FIOBJ dest, mustache_s *mustache, FIOBJ data);
```
Renders a template into an existing FIOBJ String (`dest`'s end), using the information in the `data` object.
Returns FIOBJ_INVALID if an error occurred and a FIOBJ String on success.
# 0.7.x Core Library Documentation
The core library types and functions can be found in the header `fio.h`.
The header is well documented and very long, and as a result, so is this documentation.
The header can be included more than once to produce multiple types of Hash Maps or data Sets. As well as to include some of it's optional features such as the binary String helpers and the linked list types.
## Connection (Protocol) Management
This section includes information about listening to incoming connections, connecting to remote machines and managing the protocol callback system.
The facil.io library is an evented library and the `fio_protocol_s` structure is at the core of the network evented design, so we'll start with the Protocol object.
### The `fio_protocol_s` structure
The Protocol structure defines the callbacks used for the connection and sets it's
behavior.
For concurrency reasons, a protocol instance SHOULD be unique to each connection and dynamically allocated. In single threaded applications, this is less relevant.
All the callbacks receive a unique connection ID (un-aptly named `uuid`) that can be
converted to the original file descriptor when in need.
This allows facil.io to prevent old connection handles from sending data
to new connections after a file descriptor is "recycled" by the OS.
The structure looks like this:
```c
struct fio_protocol_s {
void (*on_data)(intptr_t uuid, fio_protocol_s *protocol);
void (*on_ready)(intptr_t uuid, fio_protocol_s *protocol);
uint8_t (*on_shutdown)(intptr_t uuid, fio_protocol_s *protocol);
void (*on_close)(intptr_t uuid, fio_protocol_s *protocol);
void (*ping)(intptr_t uuid, fio_protocol_s *protocol);
size_t rsv;
};
```
#### `fio_protocol_s->on_data`
```c
void on_data(intptr_t uuid, fio_protocol_s *protocol);
```
Called when a data is available.
The function is called under the protocol's main lock (`FIO_PR_LOCK_TASK`), safeguarding the connection against collisions (the function will not run concurrently with itself for the same connection.
#### `fio_protocol_s->on_ready`
```c
void on_ready(intptr_t uuid, fio_protocol_s *protocol);
```
Called once all pending [`fio_write`](#fio_write) calls are finished.
For new connections this callback is also called once a connection was established and the connection can be written to.
#### `fio_protocol_s->on_shutdown`
```c
uint8_t on_shutdown(intptr_t uuid, fio_protocol_s *protocol);
```
Called when the server is shutting down, immediately before closing the connection.
The callback runs within a `FIO_PR_LOCK_TASK` lock, so it will never run concurrently with `on_data` or other connection specific tasks [`fio_defer_io_task`](#fio_defer_io_task).
The `on_shutdown` callback should return 0 under normal circumstances. This will mark the connection for immediate closure and allow 8 seconds for all pending data to be sent.
The `on_shutdown` callback may also return any number between 1..254 to delay the socket closure by that amount of time.
If the `on_shutdown` returns 255, the socket is ignored and it will be abruptly terminated when all other sockets have finished their graceful shutdown procedure.
#### `fio_protocol_s->on_close`
```c
uint8_t on_close(intptr_t uuid, fio_protocol_s *protocol);
```
Called when the connection was closed, but will not run concurrently with other callbacks.
#### `fio_protocol_s->ping`
```c
uint8_t ping(intptr_t uuid, fio_protocol_s *protocol);
```
Called when a connection's timeout was reached.
This callback is called outside of the protocol's normal locks to support pinging in cases where the `on_data` callback is running in the background (which shouldn't happen, but we know it sometimes does).
#### `fio_protocol_s->rsv`
This is private metadata used by facil. In essence it holds the locking data and overwriting this data is extremely volatile.
The data MUST be set to 0 before [attaching a protocol](#fio_attach) to facil.io.
### Attaching / Detaching Protocol Objects
Once a protocol object was created, it should be attached to the fail.io library.
Detaching is also possible by attaching a NULL protocol (used for "hijacking" the socket from `facil.io`).
#### `fio_attach`
```c
void fio_attach(intptr_t uuid, fio_protocol_s *protocol);
```
Attaches (or updates) a protocol object to a connection's uuid.
The new protocol object can be NULL, which will detach ("hijack") the socket.
The old protocol's `on_close` (if any) will be scheduled.
On error, the new protocol's `on_close` callback will be called immediately.
**Note**: before attaching a file descriptor that was created outside of facil.io's library, make sure it is set to non-blocking mode (see [`fio_set_non_block`](#fio_set_non_block)). facil.io file descriptors are all non-blocking and it will assumes this is the case for the attached fd.
#### `fio_attach_fd`
```c
void fio_attach_fd(int fd, fio_protocol_s *protocol);
```
Attaches (or updates) a protocol object to a file descriptor (fd).
The new protocol object can be NULL, which will detach ("hijack") the socket.
The `fd` can be one created outside of facil.io if it was set to non-blocking mode (see [`fio_set_non_block`](#fio_set_non_block)).
The old protocol's `on_close` (if any) will be scheduled.
On error, the new protocol's `on_close` callback will be called immediately.
#### `fio_capa`
```c
size_t fio_capa(void);
```
Returns the maximum number of open files facil.io can handle per worker process.
In practice, this number represents the maximum `fd` value + 1.
Total OS limits might apply as well but aren't tested or known by facil.io.
The value of 0 indicates either that the facil.io library wasn't initialized
yet or that it's resources were already released.
#### `fio_timeout_set`
```c
void fio_timeout_set(intptr_t uuid, uint8_t timeout);
```
Sets a timeout for a specific connection (only when running and valid).
#### `fio_timeout_get`
```c
uint8_t fio_timeout_get(intptr_t uuid);
```
Gets a timeout for a specific connection. Returns 0 if none.
#### `fio_touch`
```c
void fio_touch(intptr_t uuid);
```
"Touches" a socket connection, resetting it's timeout counter.
#### `fio_force_event`
```c
void fio_force_event(intptr_t uuid, enum fio_io_event event);
```
Schedules an IO event, even if it did not occur.
Possible events are:
* `FIO_EVENT_ON_DATA` - as if data is available to be read.
* `FIO_EVENT_ON_READY` - as if the connection can be written to (if there's data in the buffer, `fio_flush` will be called).
* `FIO_EVENT_ON_TIMEOUT` - as if the connection timed out (`ping`).
#### `fio_suspend`
```c
void fio_suspend(intptr_t uuid);
```
Temporarily prevents `on_data` events from firing.
Resume listening to `on_data` events by calling the `fio_force_event(uuid, FIO_EVENT_ON_DATA`.
Note: the function will work as expected when called within the protocol's `on_data` callback and the `uuid` refers to a valid socket. Otherwise the function might quietly fail.
### Listening to incoming connections
Listening to incoming connections is pretty straight forward and performed using the [`facil_listen`](#fio_listen) function.
After a new connection is accepted, the `on_open` callback passed to [`facil_listen`](#fio_listen) is called.
The `on_open` callback should allocate the new connection's protocol and call [`fio_attach`](#fio_attach) to attach a protocol to the connection's uuid.
The protocol's `on_close` callback is expected to handle any cleanup required.
The following is an example for a TCP/IP echo server using facil.io:
```c
#include
#include /* for the command line interface helpers */
/* *****************************************************************************
Echo connection callbacks
***************************************************************************** */
// A callback to be called whenever data is available on the socket
static void echo_on_data(intptr_t uuid, fio_protocol_s *prt) {
// echo buffer
char buffer[1024] = {'E', 'c', 'h', 'o', ':', ' '};
ssize_t len;
// Read to the buffer, starting after the "Echo: "
while ((len = fio_read(uuid, buffer + 6, 1018)) > 0) {
fprintf(stderr, "Read: %.*s", (int)len, buffer + 6);
// Write back the message
fio_write(uuid, buffer, len + 6);
// Handle goodbye
if ((buffer[6] | 32) == 'b' && (buffer[7] | 32) == 'y' &&
(buffer[8] | 32) == 'e') {
fio_write(uuid, "Goodbye.\n", 9);
fio_close(uuid);
return;
}
}
(void)prt; // we can ignore the unused argument
}
// A callback called whenever a timeout is reach
static void echo_ping(intptr_t uuid, fio_protocol_s *prt) {
fio_write(uuid, "Server: Are you there?\n", 23);
(void)prt; // we can ignore the unused argument
}
// A callback called if the server is shutting down...
// ... while the connection is still open
static uint8_t echo_on_shutdown(intptr_t uuid, fio_protocol_s *prt) {
fio_write(uuid, "Echo server shutting down\nGoodbye.\n", 35);
return 0;
(void)prt; // we can ignore the unused argument
}
static void echo_on_close(intptr_t uuid, fio_protocol_s *proto) {
fprintf(stderr, "Connection %p closed.\n", (void *)proto);
free(proto);
(void)uuid;
}
/* *****************************************************************************
The main echo protocol creation callback
***************************************************************************** */
// A callback called for new connections
static void echo_on_open(intptr_t uuid, void *udata) {
// Protocol objects MUST be dynamically allocated when multi-threading.
fio_protocol_s *echo_proto = malloc(sizeof(*echo_proto));
*echo_proto = (fio_protocol_s){.on_data = echo_on_data,
.on_shutdown = echo_on_shutdown,
.on_close = echo_on_close,
.ping = echo_ping};
fprintf(stderr, "New Connection %p received from %s\n", (void *)echo_proto,
fio_peer_addr(uuid).data);
fio_attach(uuid, echo_proto);
fio_write2(uuid, .data.buffer = "Echo Service: Welcome\n", .length = 22,
.after.dealloc = FIO_DEALLOC_NOOP);
fio_timeout_set(uuid, 5);
(void)udata; // ignore this
}
/* *****************************************************************************
The main function (listens to the `echo` connections and handles CLI)
***************************************************************************** */
// The main function starts listening to echo connections
int main(int argc, char const *argv[]) {
/* Setup CLI arguments */
fio_cli_start(argc, argv, 0, 0, "this example accepts the following options:",
FIO_CLI_INT("-t -thread number of threads to run."),
FIO_CLI_INT("-w -workers number of workers to run."),
"-b, -address the address to bind to.",
FIO_CLI_INT("-p,-port the port to bind to."),
FIO_CLI_BOOL("-v -log enable logging."));
/* Setup default values */
fio_cli_set_default("-p", "3000");
fio_cli_set_default("-t", "1");
fio_cli_set_default("-w", "1");
/* Listen for connections */
if (fio_listen(.port = fio_cli_get("-p"), .on_open = echo_on_open) == -1) {
perror("No listening socket available on port 3000");
exit(-1);
}
/* Run the server and hang until a stop signal is received */
fio_start(.threads = fio_cli_get_i("-t"), .workers = fio_cli_get_i("-w"));
}
```
#### `fio_listen`
```c
intptr_t fio_listen(struct fio_listen_args args);
#define fio_listen(...) fio_listen((struct fio_listen_args){__VA_ARGS__})
```
The `fio_listen` function is shadowed by the `fio_listen` MACRO, which allows the function to accept "named arguments", as shown above in the example code:
```c
if (fio_listen(.port = "3000", .on_open = echo_on_open) == -1) {
perror("No listening socket available on port 3000");
exit(-1);
}
```
The following arguments are supported:
* `on_open`:
This callback will be called whenever a new connection is accepted. It **must** either call [`fio_attach`](#fio_attach) or close the connection.
The callback should accept the new connection's uuid and a void pointer (the optional `udata` pointer one passed to `fio_listen`)
// callback example:
void on_open(intptr_t uuid, void *udata);
* `port`:
The network service / port. A NULL or "0" port indicates a Unix socket should be used.
// type:
const char *port;
* `address`:
The socket binding address. Defaults to the recommended NULL (recommended for TCP/IP).
// type:
const char *address;
* `udata`:
Opaque user data. This will be passed along to the `on_open` callback.
// type:
void *udata;
* `tls`:
A pointer to a `fio_tls_s` object, for [SSL/TLS support](https://facil.io/0.7.x/fio_tls/) (fio_tls.h).
// type:
void *tls;
* `on_start`:
A callback to be called when the server starts (or a worker process is re-spawned), allowing for further initialization, such as timed event scheduling or VM initialization.
// callback example:
void on_start(intptr_t uuid, void *udata);
* `on_finish`:
A callback to be called for every process once the listening socket is closed.
// callback example:
void on_finish(intptr_t uuid, void *udata);
### Connecting to remote servers as a client
Establishing a client connection is about as easy as setting up a listening socket, and follows, give or take, the same procedure.
#### `fio_connect`
```c
intptr_t fio_connect(struct fio_connect_args args);
#define fio_connect(...) fio_connect((struct fio_connect_args){__VA_ARGS__})
```
The `fio_connect` function is shadowed by the `fio_connect` MACRO, which allows the function to accept "named arguments", similar to [`fio_listen](#fio_listen).
The following arguments are supported:
* `port`:
The remote network service / port. A NULL or "0" port indicates a Unix socket connection should be attempted.
// type:
const char *port;
* `address`:
The remote (or Unix socket) address. Defaults to the recommended NULL (recommended for TCP/IP).
// type:
const char *address;
* `on_connect`:
This callback will be called once the new connection is established. It **must** either call [`fio_attach`](#fio_attach) or close the connection.
The callback should accept the new connection's uuid and a void pointer (the optional `udata` pointer one passed to `fio_connect`)
// callback example:
void on_connect(intptr_t uuid, void *udata);
* `on_fail`:
This callback will be called when a socket fails to connect. It's often a good place for cleanup.
The callback should accept the attempted connection's uuid (might be -1) and a void pointer (the optional `udata` pointer one passed to `fio_connect`)
// callback example:
void on_connect(intptr_t uuid, void *udata);
* `udata`:
Opaque user data. This will be passed along to the `on_connect` or `on_fail` callback.
// type:
void *udata;
* `timeout`:
A non-system timeout after which connection is assumed to have failed.
// type:
uint8_t timeout;
* `tls`:
A pointer to a `fio_tls_s` object, for [SSL/TLS support](https://facil.io/0.7.x/fio_tls/) (fio_tls.h).
// type:
void *tls;
### URL Parsing
#### `fio_url_parse`
```c
fio_url_s fio_url_parse(const char *url, size_t length);
```
Parses the URI returning it's components and their lengths - no decoding
performed, doesn't accept decoded URIs.
the result returned by `fio_url_parse` is a `fio_url_s` structure:
```c
typedef struct {
fio_str_info_s scheme;
fio_str_info_s user;
fio_str_info_s password;
fio_str_info_s host;
fio_str_info_s port;
fio_str_info_s path;
fio_str_info_s query;
fio_str_info_s target;
} fio_url_s;
```
The returned string are NOT NUL terminated, they are merely locations within the original string.
This function attempts to accept different formats, including any of the following formats:
* `/complete_path?query#target`
i.e.:
* /index.html?page=1#list
* `host:port/complete_path?query#target`
i.e.:
* example.com
* example.com:8080
* example.com/index.html
* example.com:8080/index.html
* `user:password@host:port[/path?query#target]`
i.e.:
* user:1234@example.com:8080/index.html
* user:1234@example.com?query=1
* john:1234@example.com
* `scheme://user:password@host:port/path?query#target`
i.e.:
* http://example.com/index.html?page=1#list
* tcp://example.com
Invalid formats might produce unexpected results. No error testing is performed.
### Manual Protocol Locking
#### `fio_protocol_try_lock`
```c
fio_protocol_s *fio_protocol_try_lock(intptr_t uuid, enum fio_protocol_lock_e);
```
This function allows out-of-task access to a connection's `fio_protocol_s` object by attempting to acquire a locked pointer.
CAREFUL: mostly, the protocol object will be locked and a pointer will be sent to the connection event's callback. However, if you need access to the protocol object from outside a running connection task, you might need to lock the protocol to prevent it from being closed / freed in the background.
facil.io uses three different locks (see [`fio_defer_io_task`](#fio_defer_io_task) for more information):
* `FIO_PR_LOCK_TASK` locks the protocol for normal tasks (i.e. `on_data`).
* `FIO_PR_LOCK_WRITE` locks the protocol for high priority `fio_write`
oriented tasks (i.e. `ping`, `on_ready`).
* `FIO_PR_LOCK_STATE` locks the protocol for quick operations that need to copy
data from the protocol's data structure.
IMPORTANT: Remember to call [`fio_protocol_unlock`](fio_protocol_unlock) using the same lock type.
Returns a pointer to a protocol object on success and NULL on error and setting `errno` (lock busy == `EWOULDBLOCK`, connection invalid == `EBADF`).
On error, consider calling `fio_defer` or `fio_defer_io_task` instead of busy waiting. Busy waiting SHOULD be avoided whenever possible.
#### `fio_protocol_unlock`
```c
void fio_protocol_unlock(fio_protocol_s *pr, enum fio_protocol_lock_e);
```
Don't unlock what you didn't lock with `fio_protocol_try_lock`... see [`fio_protocol_try_lock`](#fio_protocol_try_lock) for details.
## Running facil.io
The facil.io IO reactor can be started in single-threaded, multi-threaded, forked (multi-process) and hybrid (forked + multi-threads) modes.
In cluster mode (when running more than a single process), a crashed worker process will be automatically re-spawned and "hot restart" is enabled (using the USR1 signal).
#### `fio_start`
```c
void fio_start(struct fio_start_args args);
#define fio_start(...) fio_start((struct fio_start_args){__VA_ARGS__})
```
Starts the facil.io event loop. This function will return after facil.io is done (after shutdown).
This method blocks the current thread until the server is stopped (when a SIGINT/SIGTERM is received).
The `fio_start` function is shadowed by the `fio_start` MACRO, which allows the function to accept "named arguments", i.e.:
```c
fio_start(.threads = 4, .workers = 2);
```
The following arguments are supported:
* `threads`:
The number of threads to run in the thread pool.
// type:
int16_t threads;
* `workers`:
The number of worker processes to run (in addition to a root process)
This invokes facil.io's cluster mode, where a crashed worker will be automatically re-spawned and "hot restart" is enabled (using the USR1 signal).
// type:
int16_t workers;
Negative thread / worker values indicate a fraction of the number of CPU cores. i.e., -2 will normally indicate "half" (1/2) the number of cores.
If the other option (i.e. `.workers` when setting `.threads`) is zero, it will be automatically updated to reflect the option's absolute value. i.e.: if .threads == -2 and .workers == 0, than facil.io will run 2 worker processes with (cores/2) threads per process.
#### `fio_stop`
```c
void fio_stop(void);
```
Attempts to stop the facil.io application. This only works within the Root
process. A worker process will simply re-spawn itself (hot-restart).
#### `fio_expected_concurrency`
```c
void fio_expected_concurrency(int16_t *threads, int16_t *workers);
```
Returns the number of expected threads / processes to be used by facil.io.
The pointers should start with valid values that match the expected threads /
processes values passed to `fio_start`.
The data in the pointers will be overwritten with the result.
#### `fio_is_running`
```c
int16_t fio_is_running(void);
```
Returns the number of worker processes if facil.io is running (1 is returned when in single process mode, otherwise the number of workers).
Returns 0 if facil.io isn't running or is winding down (during shutdown).
#### `fio_is_worker`
```c
int fio_is_worker(void);
```
Returns 1 if the current process is a worker process or a single process.
Otherwise returns 0.
**Note**: When cluster mode is off, the root process is also the worker process. This means that single process instances don't automatically re-spawn after critical errors.
#### `fio_parent_pid`
```c
pid_t fio_parent_pid(void);
```
Returns facil.io's parent (root) process pid.
#### `fio_reap_children`
```c
void fio_reap_children(void);
```
Initializes zombie reaping for the process. Call before `fio_start` to enable
global zombie reaping.
#### `fio_signal_handler_reset`
```c
void fio_signal_handler_reset(void);
```
Resets any existing signal handlers, restoring their state to before they were set by facil.io.
This stops both child reaping (`fio_reap_children`) and the default facil.io signal handlers (i.e., CTRL-C).
This function will be called automatically by facil.io whenever facil.io stops.
#### `fio_last_tick`
```c
struct timespec fio_last_tick(void);
```
Returns the last time facil.io reviewed any pending IO events.
#### `fio_engine`
```c
char const *fio_engine(void);
```
Returns a C string detailing the IO engine selected during compilation.
Valid values are "kqueue", "epoll" and "poll".
## Socket / Connection Functions
### Creating, closing and testing sockets
#### `fio_socket`
```c
intptr_t fio_socket(const char *address, const char *port, uint8_t is_server);
```
Creates a TCP/IP or Unix socket and returns it's unique identifier.
The socket is non-blocking and the `O_CLOEXEC` flag will be set.
For TCP/IP server sockets (`is_server` is `1`), a NULL `address` variable is recommended. Use "localhost" or "127.0.0.1" to limit access to the server application.
For TCP/IP client sockets (`is_server` is `0`), a remote `address` and `port` combination will be required
For Unix server or client sockets, set the `port` variable to NULL or `0` (and the `is_server` to `1`).
Returns -1 on error. Any other value is a valid unique identifier.
**Note**: facil.io uses unique identifiers to protect sockets from collisions. However these identifiers can be converted to the underlying file descriptor using the [`fio_uuid2fd`](#fio_uuid2fd) macro.
#### `fio_accept`
```c
intptr_t fio_accept(intptr_t srv_uuid);
```
`fio_accept` accepts a new socket connection from a server socket - see the server flag on [`fio_socket`](#fio_socket).
Accepted connection are automatically set to non-blocking mode and the `O_CLOEXEC` flag is set.
**Note**: this function does NOT attach the socket to the IO reactor - see [`fio_attach`](#fio_attach).
#### `fio_is_valid`
```c
int fio_is_valid(intptr_t uuid);
```
Returns 1 if the uuid refers to a valid and open, socket.
Returns 0 if not.
#### `fio_is_closed`
```c
int fio_is_closed(intptr_t uuid);
```
Returns 1 if the uuid is invalid or the socket is flagged to be closed.
Returns 0 if the socket is valid, open and isn't flagged to be closed.
#### `fio_close`
```c
void fio_close(intptr_t uuid);
```
`fio_close` marks the connection for disconnection once all the data was sent. The actual disconnection will be managed by the [`fio_flush`](#fio_flush) function.
[`fio_flush`](#fio_flush) will be automatically scheduled.
#### `fio_force_close`
```c
void fio_force_close(intptr_t uuid);
```
`fio_force_close` closes the connection immediately, without adhering to any protocol restrictions and without sending any remaining data in the connection buffer.
#### `fio_set_non_block`
```c
int fio_set_non_block(int fd);
```
Sets a socket to non blocking state.
This will also set the `O_CLOEXEC` flag for the file descriptor.
This function is called automatically for the new socket, when using
`fio_socket`, `fio_accept`, `fio_listen` or `fio_connect`.
Call this function before attaching an `fd` that was created outside of facil.io.
#### `fio_peer_addr`
```c
fio_str_info_s fio_peer_addr(intptr_t uuid);
```
Returns the information available about the socket's peer address.
If no information is available, the structure will be initialized with zero (`.data == NULL`).
The information is only available when the socket was accepted using `fio_accept` or opened using `fio_connect`.
#### The `fio_str_info_s` return value
```c
typedef struct fio_str_info_s {
size_t capa; /* Buffer capacity, if the string is writable. */
size_t len; /* String length. */
char *data; /* Pointer to the string's first byte. */
} fio_str_info_s;
```
A string information type, reports information about a C string.
### Reading / Writing
#### `fio_read`
```c
ssize_t fio_read(intptr_t uuid, void *buffer, size_t count);
```
`fio_read` attempts to read up to count bytes from the socket into the buffer starting at `buffer`.
`fio_read`'s return values are wildly different then the native return values and they aim at making far simpler sense.
`fio_read` returns the number of bytes read (0 is a valid return value which simply means that no bytes were read from the buffer).
On a fatal connection error that leads to the connection being closed (or if the connection is already closed), `fio_read` returns -1.
The value 0 is the valid value indicating no data was read.
Data might be available in the kernel's buffer while it is not available to be read using `fio_read` (i.e., when using a transport layer, such as TLS, with Read/Write hooks).
#### `fio_write2`
```c
ssize_t fio_write2_fn(intptr_t uuid, fio_write_args_s options);
#define fio_write2(uuid, ...) \
fio_write2_fn(uuid, (fio_write_args_s){__VA_ARGS__})
```
Schedules data to be written to the socket.
`fio_write2` is similar to `fio_write`, except that it allows far more flexibility.
**Note**: The data is "moved" to the ownership of the socket, not copied. By default, `free` (not `fio_free` will be called to deallocate the data. This can be controlled by the `.after.dealloc` function pointer argument.
The following arguments are supported (in addition to the `uuid` argument):
* `data.buffer` OR `data.fd`:
The data to be sent. This can be either a block of memory or a file descriptor.
// type:
union {
/** The in-memory data to be sent. */
const void *buffer;
/** The data to be sent, if this is a file. */
const intptr_t fd;
} data;
* `after.dealloc` OR `after.close`:
The deallocation function. This function will be called to either free the memory of close the file once the data was sent.
The default for memory is the system's `free` and for files the system `close` is called (using a wrapper function).
A no-operation (do nothing) function is provided for sending static data: `FIO_DEALLOC_NOOP`. Use this: `.after.dealloc = FIO_DEALLOC_NOOP`
Note: connection library functions MUST NEVER be called by a callback, or a deadlock might occur.
// type:
union {
void (*dealloc)(void *buffer);
void (*close)(intptr_t fd);
} after;
* `length`:
The length (size) of the buffer, or the amount of data to be sent from the file descriptor.
// type:
uintptr_t length;
* `offset`:
Starting point offset from the buffer or file descriptor's beginning.
// type:
uintptr_t offset;
* `urgent`:
The data will be sent as soon as possible, moving forward in the connection's queue as much as possible without fragmenting other `fio_write2` calls.
// type:
unsigned urgent : 1;
* `is_fd`:
The `data` union contains the value of a file descriptor (`intptr_t`). i.e.: `.data.fd = fd` or `.data.buffer = (void*)fd;`
// type:
unsigned is_fd : 1;
On error, -1 will be returned. Otherwise returns 0.
#### `fio_write`
```c
inline ssize_t fio_write(const intptr_t uuid, const void *buffer,
const size_t length);
```
`fio_write` copies `legnth` data from the buffer and schedules the data to
be sent over the socket.
On error, -1 will be returned. Otherwise returns 0.
Returns the same values as `fio_write2` and is equivalent to:
```c
inline ssize_t fio_write(const intptr_t uuid, const void *buffer,
const size_t length) {
if (!length || !buffer)
return 0;
void *cpy = fio_malloc(length);
if (!cpy)
return -1;
memcpy(cpy, buffer, length);
return fio_write2(uuid, .data.buffer = cpy, .length = length,
.after.dealloc = fio_free);
}
```
#### `fio_sendfile`
```c
inline static ssize_t fio_sendfile(intptr_t uuid, intptr_t source_fd,
off_t offset, size_t length);
```
Sends data from a file as if it were a single atomic packet (sends up to `length` bytes or until EOF is reached).
Once the file was sent, the `source_fd` will be closed using `close`.
The file will be buffered to the socket chunk by chunk, so that memory consumption is capped. The system's `sendfile` might be used if conditions permit.
`offset` dictates the starting point for the data to be sent and length sets the maximum amount of data to be sent.
Returns -1 and closes the file on error. Returns 0 on success.
Returns the same values as `fio_write2` and is equivalent to:
```c
inline ssize_t fio_sendfile(intptr_t uuid, intptr_t source_fd,
off_t offset, size_t length) {
return fio_write2(uuid, .data.fd = source_fd, .length = length, .is_fd = 1,
.offset = offset);
}
```
#### `fio_pending`
```c
size_t fio_pending(intptr_t uuid);
```
Returns the number of `fio_write` calls that are waiting in the connection's queue and haven't been processed.
#### `fio_flush`
```c
ssize_t fio_flush(intptr_t uuid);
```
`fio_flush` attempts to write any remaining data in the internal buffer to the underlying file descriptor and closes the underlying file descriptor once if it's marked for closure (and all the data was sent).
Return values: 1 will be returned if data remains in the buffer. 0 will be returned if the buffer was fully drained. -1 will be returned on an error or when the connection is closed.
`errno` will be set to EWOULDBLOCK if the socket's lock is busy.
#### `fio_flush_strong`
```c
#define fio_flush_strong(uuid) \
do { \
errno = 0; \
} while (fio_flush(uuid) > 0 || errno == EWOULDBLOCK)
```
Blocks until all the data was flushed from the buffer.
#### `fio_flush_all`
```c
size_t fio_flush_all(void);
```
`fio_flush_all` attempts flush all the open connections.
Returns the number of sockets still in need to be flushed.
#### `fio_uuid2fd`
```c
#define fio_uuid2fd(uuid) ((int)((uintptr_t)uuid >> 8))
```
Convert between a facil.io connection's identifier (uuid) and system's fd.
#### `fio_fd2uuid`
```c
intptr_t fio_fd2uuid(int fd);
```
`fio_fd2uuid` takes an existing file decriptor `fd` and returns it's active `uuid`.
If the file descriptor was closed, **it will be registered as open**.
If the file descriptor was closed directly (not using `fio_close`) or the closure event hadn't been processed, a false positive will be possible.
This is not an issue, since the use of an invalid fd will result in the registry being updated and the fd being closed.
Returns -1 on error. Returns a valid socket (non-random) UUID.
### Connection Object Links
Connection object links can links an object to a connection's lifetime rather than it's Protocol's lifetime.
This is can be useful and is used internally by the `fio_subscribe` function to attach subscriptions to connections (when requested).
#### `fio_uuid_link`
```c
void fio_uuid_link(intptr_t uuid, void *obj, void (*on_close)(void *obj));
```
Links an object to a connection's lifetime, calling the `on_close` callback once the connection has died.
If the `uuid` is invalid, the `on_close` callback will be called immediately.
**Note**: the `on_close` callback will be called with high priority. Long tasks should be deferred using [`fio_defer`](#fio_defer).
#### `fio_uuid_unlink`
```c
void fio_uuid_unlink(intptr_t uuid, void *obj);
```
Un-links an object from the connection's lifetime, so it's `on_close` callback will NOT be called.
### Lower-Level: Read / Write / Close Hooks
facil.io's behavior can be altered to support complex networking needs, such as SSL/TLS integration.
This can be achieved using connection hooks for the common read/write/close operations.
To do so, a `fio_rw_hook_s` object must be created (a static object can be used as well).
```c
typedef struct fio_rw_hook_s {
ssize_t (*read)(intptr_t uuid, void *udata, void *buf, size_t count);
ssize_t (*write)(intptr_t uuid, void *udata, const void *buf, size_t count);
ssize_t (*flush)(intptr_t uuid, void *udata);
ssize_t (*before_close)(intptr_t uuid, void *udata);
void (*cleanup)(void *udata);
} fio_rw_hook_s;
```
* The `read` hook callback:
This callback should implement the reading function from the file descriptor. It must behave the same as the system's `read` call, including the setting `errno` to `EAGAIN` / `EWOULDBLOCK`.
Note: facil.io library functions MUST NEVER be called by any r/w hook, or a deadlock might occur.
* The `write` hook callback:
This callback should implement the writing function to the file descriptor. It must behave like the file system's `write` call, including the setting `errno` to `EAGAIN` / `EWOULDBLOCK`.
The function is expected to call the `flush` callback (or it's logic) internally. Either `write` **or** `flush` are called, but not both.
Note: facil.io library functions MUST NEVER be called by any r/w hook, or a deadlock might occur.
* The `flush` hook callback:
When implemented, this function will be called to flush any data remaining in the read/write hook's internal buffer.
This callback should return the number of bytes remaining in the internal buffer (0 is a valid response) or -1 (on error).
Note: facil.io library functions MUST NEVER be called by any r/w hook, or a deadlock might occur.
* The `before_close` hook callback:
The `before_close` callback is called only once before closing the `uuid` and it might not get called if an abnormal closure is detected.
If the function returns a non-zero value, than closure will be delayed until the `flush` returns 0 (or less). This allows a closure signal to be sent by the read/write hook when such a signal is required.
Note: facil.io library functions MUST NEVER be called by any r/w hook, or a deadlock might occur.
* The `cleanup` hook callback:
Called when the hook is removed from the `uuid`, either because of a call to `fio_rw_hook_set` or because the connection was closed.
This callback is always called, even if `fio_rw_hook_set` fails.
#### `fio_rw_hook_set`
```c
int fio_rw_hook_set(intptr_t uuid, fio_rw_hook_s *rw_hooks, void *udata);
```
Sets a connection's hook callback object (`fio_rw_hook_s`).
Returns 0 on success or -1 on error (closed / invalid `uuid`).
If the function fails, than the `cleanup` callback will be called before the function returns.
#### `fio_rw_hook_replace_unsafe`
```c
int fio_rw_hook_replace_unsafe(intptr_t uuid, fio_rw_hook_s *rw_hooks, void *udata);
```
Replaces an existing read/write hook with another from within a read/write hook callback.
Returns 0 on success or -1 on error (closed / invalid `uuid`).
Does NOT call any cleanup callbacks.
Replaces existing `udata`. Call with the existing `udata` to keep it.
**Note**: this function is marked as unsafe, since it should only be called from within an existing read/write hook callback. Otherwise, data corruption might occur.
#### `FIO_DEFAULT_RW_HOOKS`
```c
extern const fio_rw_hook_s FIO_DEFAULT_RW_HOOKS;
```
The default Read/Write hooks used for system Read/Write (`udata` == `NULL`).
## Event / Task scheduling
facil.io allows a number of ways to schedule events / tasks:
* Queue - schedules an event / task to be performed as soon as possible.
* Timer - the event / task will be scheduled in the Queue after the designated period.
* State - the event / task will be called during a specific change in facil.io's state (starting up, cleaning up, etc').
### The Task Queue Functions
#### `fio_defer`
```c
int fio_defer(void (*task)(void *, void *), void *udata1, void *udata2);
```
Defers a task's execution.
The task will be executed after all currently scheduled tasks (placed at the end of the scheduling queue).
Tasks are functions of the type `void task(void *, void *)`, they return nothing (void) and accept two opaque `void *` pointers, user-data 1 (`udata1`) and user-data 2 (`udata2`).
Returns -1 or error, 0 on success.
#### `fio_defer_perform`
```c
void fio_defer_perform(void);
```
Performs all deferred tasks.
#### `fio_defer_has_queue`
```c
int fio_defer_has_queue(void);
```
Returns true if there are deferred functions waiting for execution.
### Timer Functions
#### `fio_run_every`
```c
int fio_run_every(size_t milliseconds, size_t repetitions, void (*task)(void *),
void *arg, void (*on_finish)(void *));
```
Creates a timer to run a task at the specified interval.
Timer tasks accept only a single user data pointer (`udata` ).
The task will repeat `repetitions` times. If `repetitions` is set to 0, task
will repeat forever.
The `on_finish` handler is always called (even on error).
Returns -1 on error.
### Connection task scheduling
Connection tasks are performed within one of the connection's locks (`FIO_PR_LOCK_TASK`, `FIO_PR_LOCK_WRITE`, `FIO_PR_LOCK_STATE`), assuring a measure of safety.
#### `fio_defer_io_task`
```c
void fio_defer_io_task(intptr_t uuid, fio_defer_iotask_args_s args);
#define fio_defer_io_task(uuid, ...) \
fio_defer_connection_task((uuid), (fio_defer_iotask_args_s){__VA_ARGS__})
```
This function schedules an IO task using the specified lock type.
This function is shadowed by a macro, allowing it to accept named arguments, much like [fio_start](#fio_start). The following arguments are recognized:
* `type`:
The type of lock that should be used to protect the IO task. Defaults to `FIO_PR_LOCK_TASK` (see later).
// type:
enum fio_protocol_lock_e type;
* `task`:
The task (function) to be performed. The tasks accepts the connection's `uuid`, a pointer to the protocol object and the opaque `udata` pointer passed to `fio_defer_io_task`.
// Callback example:
void task(intptr_t uuid, fio_protocol_s *, void *udata);
* `fallback`:
A fallback task (function) to be performed in cases where the `uuid` isn't valid by the time the task should be executed. The fallback task accepts the connection's `uuid` and the opaque `udata` pointer passed to `fio_defer_io_task`.
// Callback example:
void fallback(intptr_t uuid, void *udata);
* `udata`:
An opaque pointer that's passed along to the task.
// type:
void *udata;
Lock types are one of the following:
* `FIO_PR_LOCK_TASK` - a task lock locks might change data owned by the protocol object. This task is used for tasks such as `on_data`.
* `FIO_PR_LOCK_WRITE` - a lock that promises only to use static data (data that tasks never changes) in order to write to the underlying socket. This lock is used for tasks such as `on_ready` and `ping`
* `FIO_PR_LOCK_STATE` - a lock that promises only to retrieve static data (data that tasks never changes), performing no actions. This usually isn't used for client side code (used internally by facil) and is only meant for very short locks.
### Startup / State Tasks (fork, start up, idle, etc')
facil.io allows callbacks to be called when certain events occur (such as before and after forking etc').
Callbacks will be called from last to first (last callback added executes first), allowing for logical layering of dependencies.
During an event, changes to the callback list are ignored (callbacks can't remove other callbacks for the same event).
#### `callback_type_e`- State callback timing type
The `fio_state_callback_*` functions manage callbacks for a specific timing. Valid timings values are:
* `FIO_CALL_ON_INITIALIZE`: Called once during library initialization.
* `FIO_CALL_PRE_START`: Called once before starting up the IO reactor.
* `FIO_CALL_BEFORE_FORK`: Called before each time the IO reactor forks a new worker.
* `FIO_CALL_AFTER_FORK`: Called after each fork (both in parent and workers).
* `FIO_CALL_IN_CHILD`: Called by a worker process right after forking (and after `FIO_CALL_AFTER_FORK`).
* `FIO_CALL_IN_MASTER`: Called by the root / master process after forking (and after `FIO_CALL_AFTER_FORK`).
* `FIO_CALL_ON_START`: Called every time a *Worker* proceess starts.
* `FIO_CALL_ON_IDLE`: Called when facil.io enters idling mode (idle callbacks might be performed out of order).
* `FIO_CALL_ON_SHUTDOWN`: Called before starting the shutdown sequence.
* `FIO_CALL_ON_FINISH`: Called just before finishing up (both on chlid and parent processes).
* `FIO_CALL_ON_PARENT_CRUSH`: Called by each worker the moment it detects the master process crashed.
* `FIO_CALL_ON_CHILD_CRUSH`: Called by the parent (master) after a worker process crashed.
* `FIO_CALL_AT_EXIT`: An alternative to the system's at_exit.
Callbacks will be called using logical order for build-up and tear-down.
During initialization related events, FIFO will be used (first in/scheduled, first out/executed).
During shut-down related tasks, LIFO will be used (last in/scheduled, first out/executed).
Idling callbacks are scheduled rather than performed during the event, so they might be performed out of order or concurrently when running in multi-threaded mode.
#### `fio_state_callback_add`
```c
void fio_state_callback_add(callback_type_e, void (*func)(void *), void *arg);
```
Adds a callback to the list of callbacks to be called for the event.
Callbacks will be called using logical order for build-up and tear-down, according to the event's context.
#### `fio_state_callback_remove`
```c
int fio_state_callback_remove(callback_type_e, void (*func)(void *), void *arg);
```
Removes a callback from the list of callbacks to be called for the event.
Callbacks will be called using logical order for build-up and tear-down, according to the event's context.
#### `fio_state_callback_force`
```c
void fio_state_callback_force(callback_type_e);
```
Forces all the existing callbacks to run, as if the event occurred.
Callbacks will be called using logical order for build-up and tear-down, according to the event's context.
During an event, changes to the callback list are ignored (callbacks can't remove other callbacks for the same event).
#### `fio_state_callback_clear`
```c
void fio_state_callback_clear(callback_type_e);
```
Clears all the existing callbacks for the event (doesn't effect a currently firing event).
## Pub/Sub Services
facil.io supports a [Publish–Subscribe Pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) API which can be used for Inter Process Communication (IPC), messaging, horizontal scaling and similar use-cases.
### Subscription Control
#### `fio_subscribe`
```c
subscription_s *fio_subscribe(subscribe_args_s args);
#define fio_subscribe(...) fio_subscribe((subscribe_args_s){__VA_ARGS__})
```
This function subscribes to either a numerical "filter" or a named channel (but not both).
The `fio_subscribe` function is shadowed by the `fio_subscribe` MACRO, which allows the function to accept "named arguments", as shown in the following example:
```c
static void my_message_handler(fio_msg_s *msg);
//...
subscription_s * s = fio_subscribe(.channel = {.data="name", .len = 4},
.on_message = my_message_handler);
```
The function accepts the following named arguments:
* `filter`:
If `filter` is set, all messages that match the filter's numerical value will be forwarded to the subscription's callback.
Subscriptions can either require a match by filter or match by channel. This will match the subscription by filter.
**Note**: filter based messages are considered internal. They aren't shared with external pub/sub services (such as Redis) and they are ignored by meta-data callbacks (both subjects are covered later on).
// type:
int32_t filter;
* `channel`:
If `filter == 0` (or unset), than the subscription will be made using `channel` binary name matching. Note that and empty string (NULL pointer and 0 length) is a valid channel name.
Subscriptions can either require a match by filter or match by channel. This will match the subscription by channel (only messages with no `filter` will be received).
// type:
fio_str_info_s channel;
* `match`:
If an optional `match` callback is provided, pattern matching will be used as an alternative to exact channel name matching.
A single matching function is bundled with facil.io (`FIO_MATCH_GLOB`), which follows the Redis matching logic.
This is significantly slower, as no Hash Map can be used to locate a match - each message published to a channel will be tested for a match against each pattern.
// callback example:
int foo_bar_match_fn(fio_str_info_s pattern, fio_str_info_s channel);
// type:
typedef int (*fio_match_fn)(fio_str_info_s pattern, fio_str_info_s channel);
fio_match_fn match;
extern fio_match_fn FIO_MATCH_GLOB;
* `on_message`:
The callback will be called for each message forwarded to the subscription.
// callback example:
void on_message(fio_msg_s *msg);
* `on_unsubscribe`:
The callback will be called once the subscription is canceled, allowing it's resources to be freed.
// callback example:
void (*on_unsubscribe)(void *udata1, void *udata2);
* `udata1` and `udata2`:
These are the opaque user data pointers passed to `on_message` and `on_unsubscribe`.
// type:
void *udata1;
void *udata2;
The function returns a pointer to the opaque subscription type `subscription_s`.
On error, `NULL` will be returned and the `on_unsubscribe` callback will be called.
The `on_message` should accept a pointer to the `fio_msg_s` type:
```c
typedef struct fio_msg_s {
int32_t filter;
fio_str_info_s channel;
fio_str_info_s msg;
void *udata1;
void *udata2;
uint8_t is_json;
} fio_msg_s;
```
* `filter` is the numerical filter (if any) used in the `fio_subscribe` and `fio_publish` functions. Negative values are reserved and 0 == channel name matching.
* `channel` is an immutable binary string containing the channel name given to `fio_publish`. See the [`fio_str_info_s` return value](#the-fio_str_info_s-return-value) for details.
* `msg` is an immutable binary string containing the message data given to `fio_publish`. See the [`fio_str_info_s` return value](#the-fio_str_info_s-return-value) for details.
* `is_json` is a binary flag (1 or 0) that marks the message as JSON. This is the flag passed as passed to the `fio_publish` function.
* `udata1` and `udata2` are the opaque user data pointers passed to `fio_subscribe` during the subscription.
**Note (1)**: if a subscription object is no longer required, i.e., if `fio_unsubscribe` will only be called once a connection was closed or once facil.io is shutting down, consider using [`fio_uuid_link`](#fio_uuid_link) or [`fio_state_callback_add`](#fio_state_callback_add) to control the subscription's lifetime.
**Note (2)**: passing protocol object pointers to the `udata` is not safe, since protocol objects might be destroyed or invalidated due to either network events (socket closure) or internal changes (i.e., `fio_attach` being called). The preferred way is to add the `uuid` to the `udata` field and call [`fio_protocol_try_lock`](#fio_protocol_try_lock) to access the protocol object.
#### `fio_subscription_channel`
```c
fio_str_info_s fio_subscription_channel(subscription_s *subscription);
```
This helper returns a temporary String with the subscription's channel (or a binary
string representing the filter).
To keep the string beyond the lifetime of the subscription, copy the string.
#### `fio_message_defer`
```c
void fio_message_defer(fio_msg_s *msg);
```
Defers the subscription's callback handler, so the subscription will be called again for the same message.
A classic use case allows facil.io to handle other events while waiting on a lock / mutex to become available in a multi-threaded environment.
#### `fio_unsubscribe`
```c
void fio_unsubscribe(subscription_s *subscription);
```
Cancels an existing subscription.
This function will block if a subscription task is running on a different thread.
The subscription task won't be called after the function returns.
### Publishing messages
#### `fio_publish`
```c
void fio_publish(fio_publish_args_s args);
```
This function publishes a message to either a numerical "filter" or a named channel (but not both).
The message can be a `NULL` or an empty message.
The `fio_publish` function is shadowed by the `fio_publish` MACRO, which allows the function to accept "named arguments", as shown in the following example:
```c
fio_publish(.channel = {.data="name", .len = 4},
.message = {.data = "foo", .len = 3});
```
The function accepts the following named arguments:
* `filter`:
If `filter` is set, all messages that match the filter's numerical value will be forwarded to the subscription's callback.
Subscriptions can either require a match by filter or match by channel. This will match the subscription by filter.
// type:
int32_t filter;
* `channel`:
If `filter == 0` (or unset), than the subscription will be made using `channel` binary name matching. Note that and empty string (NULL pointer and 0 length) is a valid channel name.
Subscriptions can either require a match by filter or match by channel. This will match the subscription by channel (only messages with no `filter` will be received).
// type:
fio_str_info_s channel;
* `message`:
The message data to be sent.
// type:
fio_str_info_s message;
* `is_json`:
A flag indicating if the message is JSON data or binary/text.
// type:
uint8_t is_json;
* `engine`:
The pub/sub engine that should be used to forward this message (see later). Defaults to `FIO_PUBSUB_DEFAULT` (or `FIO_PUBSUB_CLUSTER`).
Pub/Sub engines dictate the behavior of the pub/sub instructions. The possible internal pub/sub engines are:
* `FIO_PUBSUB_CLUSTER` - used to publish the message to all clients in the cluster.
* `FIO_PUBSUB_PROCESS` - used to publish the message only within the current process.
* `FIO_PUBSUB_SIBLINGS` - used to publish the message except within the current process.
* `FIO_PUBSUB_ROOT` - used to publish the message exclusively to the root / master process.
The default pub/sub can be changed globally by assigning a new default engine to the `FIO_PUBSUB_DEFAULT` global variable.
// type:
fio_pubsub_engine_s const *engine;
// default engine:
extern fio_pubsub_engine_s *FIO_PUBSUB_DEFAULT;
### Pub/Sub Message MiddleWare Meta-Data
It's possible to attach meta-data to facil.io pub/sub messages before they are published.
This is only available for named channels (filter == 0).
This allows, for example, messages to be encoded as network packets for
outgoing protocols (i.e., encoding for WebSocket transmissions), improving
performance in large network based broadcasting.
**Note**: filter based messages are considered internal. They aren't shared with external pub/sub services (such as Redis) and they are ignored by meta-data callbacks.
#### `fio_message_metadata`
```c
void *fio_message_metadata(fio_msg_s *msg, intptr_t type_id);
```
Finds the message's meta-data by the meta-data's type ID. Returns the data or NULL.
#### `fio_message_metadata_callback_set`
```c
// The function:
void fio_message_metadata_callback_set(fio_msg_metadata_fn callback,
int enable);
// The callback type:
typedef fio_msg_metadata_s (*fio_msg_metadata_fn)(fio_str_info_s ch,
fio_str_info_s msg,
uint8_t is_json);
// Example callback
fio_msg_metadata_s foo_metadata(fio_str_info_s ch,
fio_str_info_s msg,
uint8_t is_json);
```
The callback should return a `fio_msg_metadata_s` object. The object looks like this:
```c
typedef struct fio_msg_metadata_s fio_msg_metadata_s;
struct fio_msg_metadata_s {
/** A type ID used to identify the meta-data. Negative values are reserved. */
intptr_t type_id;
/** Called by facil.io to cleanup the meta-data resources. */
void (*on_finish)(fio_msg_s *msg, void *metadata);
/** The pointer to be disclosed to the subscription client. */
void *metadata;
/** RESERVED for internal use (Metadata linked list): */
fio_msg_metadata_s *next;
};
```
If the the returned `.metadata` field is NULL than the result will be ignored.
To remove a callback, set the `enable` flag to false (`0`).
The cluster messaging system allows some messages to be flagged as JSON and this flag is available to the meta-data callback.
### External Pub/Sub Services
facil.io can be linked with external Pub/Sub services using "engines" (`fio_pubsub_engine_s`).
Pub/Sub engines dictate the behavior of the pub/sub instructions.
This allows for an application to connect to a service such as Redis or NATs for horizontal pub/sub scaling.
A [Redis engine](https://facil.io/0.7.x/redis/) is bundled as part of the facio.io extensions but isn't part of the core library.
The default engine can be set using the `FIO_PUBSUB_DEFAULT` global variable. It's initial default is `FIO_PUBSUB_CLUSTER` (see [`fio_publish`](#fio_publish)).
**Note**: filter based messages are considered internal. They aren't shared with external pub/sub services (such as Redis) and they are ignored by meta-data callbacks.
Engines MUST provide the listed function pointers and should be attached using the `fio_pubsub_attach` function.
Engines should disconnect / detach, before being destroyed, by using the `fio_pubsub_detach` function.
When an engine received a message to publish, it should call the `fio_publish` function with the engine to which the message is forwarded. i.e.:
```c
fio_publish(
.engine = FIO_PROCESS_ENGINE,
.channel = {0, 4, "name"},
.message = {0, 4, "data"} );
```
#### `fio_pubsub_attach`
```c
void fio_pubsub_attach(fio_pubsub_engine_s *engine);
```
Attaches an engine, so it's callbacks can be called by facil.io.
The `subscribe` callback will be called for every existing channel.
The engine type defines the following callback:
* `subscribe` - Should subscribe channel. Failures are ignored.
void subscribe(const fio_pubsub_engine_s *eng,
fio_str_info_s channel,
fio_match_fn match);
* `unsubscribe` - Should unsubscribe channel. Failures are ignored.
void unsubscribe(const fio_pubsub_engine_s *eng,
fio_str_info_s channel,
fio_match_fn match);
* `publish` - Should publish a message through the engine. Failures are ignored.
int publish(const fio_pubsub_engine_s *eng,
fio_str_info_s channel,
fio_str_info_s msg, uint8_t is_json);
**Note**: the root (master) process will call `subscribe` for any channel **in any process**, while all the other processes will call `subscribe` only for their own channels. This allows engines to use the root (master) process as an exclusive subscription process.
**IMPORTANT**: The `subscribe` and `unsubscribe` callbacks are called from within an internal lock. They MUST NEVER call pub/sub functions except by exiting the lock using `fio_defer`.
#### `fio_pubsub_detach`
```c
void fio_pubsub_detach(fio_pubsub_engine_s *engine);
```
Detaches an engine, so it could be safely destroyed.
#### `fio_pubsub_reattach`
```c
void fio_pubsub_reattach(fio_pubsub_engine_s *eng);
```
Engines can ask facil.io to call the `subscribe` callback for all active channels.
This allows engines that lost their connection to their Pub/Sub service to resubscribe all the currently active channels with the new connection.
CAUTION: This is an evented task... try not to free the engine's memory while re-subscriptions are under way...
**Note**: the root (master) process will call `subscribe` for any channel **in any process**, while all the other processes will call `subscribe` only for their own channels. This allows engines to use the root (master) process as an exclusive subscription process.
**IMPORTANT**: The `subscribe` and `unsubscribe` callbacks are called from within an internal lock. They MUST NEVER call pub/sub functions except by exiting the lock using `fio_defer`.
#### `fio_pubsub_is_attached`
```c
int fio_pubsub_is_attached(fio_pubsub_engine_s *engine);
```
Returns true (1) if the engine is attached to the system.
## The Custom Memory Allocator
facil.io includes a custom memory allocator designed for network application use.
The allocator is very fast (tested with `tests/malloc_speed.c`) and protects against fragmentation issues when used correctly (when abused, fragmentation will increase).
Allocated memory is always zeroed out and aligned on a 16 byte boundary.
Reallocated memory is always aligned on a 16 byte boundary but it might be filled with junk data after the valid data (this can be minimized by using [`fio_realloc2`](#fio_realloc2)).
Memory allocation overhead is ~ 0.1% (1/1000 per byte, or 32 bytes per 32Kb). In addition there's a small per-process overhead for the allocator's state-machine (1 page / 4Kb per process).
The memory allocator assumes multiple concurrent allocation/deallocation, short to medium life spans (memory is freed shortly, but not immediately, after it was allocated) and relatively small allocations (anything over 16Kb is forwarded to `mmap`).
This allocator should prevent memory fragmentation when allocating memory for shot / medium object life-spans (classic network use).
The memory allocator can be used in conjuncture with the system's `malloc` to minimize heap fragmentation (long-life objects use `malloc`, short life objects use `fio_malloc`).
Long term allocation can use `fio_mmap` to directly allocate memory from the system. The overhead for `fio_mmap` is 16 bytes per allocation (freed with `fio_free`).
**Note**: this custom allocator could increase memory fragmentation if long-life allocations are performed periodically (rather than performed during startup). Use [`fio_mmap`](#fio_mmap) or the system's `malloc` for long-term allocations.
### Memory Allocator Overview
The memory allocator uses `mmap` to collect memory from the system.
Each allocation collects ~8Mb from the system, aligned on a 32Kb alignment boundary (except direct `mmap` allocation for large `fio_malloc` or `fio_mmap` calls). This memory is divided into 32Kb blocks which are added to a doubly linked "free" list.
The allocator utilizes per-CPU arenas / bins to allow for concurrent memory allocations across threads and to minimize lock contention.
Each arena / bin collects a 32Kb block and allocates "slices" as required by `fio_malloc`/`fio_realloc`.
The `fio_free` function will free the whole 32Kb block as a single unit once the whole of the allocations for that block were freed (no small-allocation "free list" and no per-slice meta-data).
The memory collected from the system (the 8Mb) will be returned to the system once all the memory was both allocated and freed (or during cleanup).
To replace the system's `malloc` function family compile with the `FIO_OVERRIDE_MALLOC` defined (`-DFIO_OVERRIDE_MALLOC`).
It should be possible to use tcmalloc or jemalloc alongside facil.io's allocator.It's also possible to prevent facil.io's custom allocator from compiling by defining `FIO_FORCE_MALLOC` (`-DFIO_FORCE_MALLOC`).
More details in the `fio.h` header.
### The Memory Allocator's API
The functions were designed to be a drop in replacement to the system's memory allocation functions (`malloc`, `free` and friends).
Where some improvement could be made, it was made using an added function name to add improved functionality (such as `fio_realloc2`).
#### `fio_malloc`
```c
void *fio_malloc(size_t size)
```
Allocates memory using a per-CPU core block memory pool.
Memory is always zeroed out.
Allocations above `FIO_MEMORY_BLOCK_ALLOC_LIMIT` (16Kb bytes when using the default 32Kb blocks) will be redirected to `mmap`, as if `fio_mmap` was called.
#### `fio_calloc`
```c
void *fio_calloc(size_t size_per_unit, size_t unit_count)
```
Same as calling `fio_malloc(size_per_unit * unit_count)`;
Allocations above `FIO_MEMORY_BLOCK_ALLOC_LIMIT` (16Kb bytes when using the default 32Kb blocks) will be redirected to `mmap`, as if `fio_mmap` was called.
#### `fio_free`
```c
void fio_free(void *ptr);
```
Frees memory that was allocated using this library.
#### `fio_realloc`
```c
void *fio_realloc(void *ptr, size_t new_size);
```
Re-allocates memory. An attempt to avoid copying the data is made only for big
memory allocations (larger than `FIO_MEMORY_BLOCK_ALLOC_LIMIT`).
#### `fio_realloc2`
```c
void *fio_realloc2(void *ptr, size_t new_size, size_t copy_length);
```
Re-allocates memory. An attempt to avoid copying the data is made only for big
memory allocations (larger than `FIO_MEMORY_BLOCK_ALLOC_LIMIT`).
This variation is slightly faster as it might copy less data.
####
```c
void *fio_mmap(size_t size);
```
Allocates memory directly using `mmap`, this is prefered for objects that
both require almost a page of memory (or more) and expect a long lifetime.
However, since this allocation will invoke the system call (`mmap`), it will
be inherently slower.
`fio_free` can be used for deallocating the memory.
## Linked Lists
Linked list helpers are inline functions that become available when (and if) the `fio_h` file is included with the `FIO_INCLUDE_LINKED_LIST` macro.
This can be performed even after if the `fio.h` file was previously included. i.e.:
```c
#include // No linked list helpers
#define FIO_INCLUDE_LINKED_LIST
#include // Linked list helpers become available
```
Linked lists come in two types:
1. Independent Object Lists:
Used when a single object might belong to more than a single list, or when the object can't be edited.
1. Embedded Linked Lists
Used when a single object always belongs to a single list and can be edited to add a `node` filed. This improves memory locality and performance, as well as minimizes memory allocations.
### Independent Linked List API
The independent object lists uses the following type:
```c
/** an independent linked list. */
typedef struct fio_ls_s {
struct fio_ls_s *prev;
struct fio_ls_s *next;
const void *obj;
} fio_ls_s;
```
It can be initialized using the `FIO_LS_INIT` macro.
#### `FIO_LS_INIT`
```c
#define FIO_LS_INIT(name) \
{ .next = &(name), .prev = &(name) }
```
Initializes the list container. i.e.:
```c
fio_ls_s my_list = FIO_LS_INIT(my_list);
```
#### `fio_ls_push`
```c
inline fio_ls_s *fio_ls_push(fio_ls_s *pos, const void *obj);
```
Adds an object to the list's head.
Returns a pointer to the object's position (can be used in `fio_ls_remove`).
#### `fio_ls_unshift`
```c
inline fio_ls_s *fio_ls_unshift(fio_ls_s *pos, const void *obj);
```
Adds an object to the list's tail.
Returns a pointer to the object's position (can be used in `fio_ls_remove`).
#### `fio_ls_pop`
```c
inline void *fio_ls_pop(fio_ls_s *list);
```
Removes an object from the list's head.
#### `fio_ls_shift`
```c
inline void *fio_ls_shift(fio_ls_s *list);
```
Removes an object from the list's tail.
#### `fio_ls_remove`
```c
inline void *fio_ls_remove(fio_ls_s *node);
```
Removes a node from the list, returning the contained object.
#### `fio_ls_is_empty`
```c
inline int fio_ls_is_empty(fio_ls_s *list);
```
Tests if the list is empty.
#### `fio_ls_any`
```c
inline int fio_ls_any(fio_ls_s *list);
```
Tests if the list is NOT empty (contains any nodes).
#### `FIO_LS_FOR`
```c
#define FIO_LS_FOR(list, pos)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you please).
### Embedded Linked List API
The embedded object lists uses the following node type:
```c
/** an embeded linked list. */
typedef struct fio_ls_embd_s {
struct fio_ls_embd_s *prev;
struct fio_ls_embd_s *next;
} fio_ls_embd_s;
```
It can be initialized using the [`FIO_LS_INIT`](#fio_ls_init) macro (see above).
#### `fio_ls_embd_push`
```c
inline void fio_ls_embd_push(fio_ls_embd_s *dest, fio_ls_embd_s *node);
```
Adds a node to the list's head.
#### `fio_ls_embd_unshift`
```c
inline void fio_ls_embd_unshift(fio_ls_embd_s *dest, fio_ls_embd_s *node);
```
Adds a node to the list's tail.
#### `fio_ls_embd_pop`
```c
inline fio_ls_embd_s *fio_ls_embd_pop(fio_ls_embd_s *list);
```
Removes a node from the list's head.
#### `fio_ls_embd_shift`
```c
inline fio_ls_embd_s *fio_ls_embd_shift(fio_ls_embd_s *list);
```
Removes a node from the list's tail.
#### `fio_ls_embd_remove`
```c
inline fio_ls_embd_s *fio_ls_embd_remove(fio_ls_embd_s *node);
```
Removes a node from the containing node.
#### `fio_ls_embd_is_empty`
```c
inline int fio_ls_embd_is_empty(fio_ls_embd_s *list);
```
Tests if the list is empty.
#### `fio_ls_embd_any`
```c
inline int fio_ls_embd_any(fio_ls_embd_s *list);
```
Tests if the list is NOT empty (contains any nodes).
#### `FIO_LS_EMBD_FOR`
```c
#define FIO_LS_EMBD_FOR(list, node)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you please).
#### `FIO_LS_EMBD_OBJ`
```c
#define FIO_LS_EMBD_OBJ(type, member, plist) \
((type *)((uintptr_t)(plist) - (uintptr_t)(&(((type *)0)->member))))
```
Takes a list pointer `plist` (node) and returns a pointer to it's container.
This uses pointer offset calculations and can be used to calculate any structure's pointer (not just list containers) as an offset from a pointer of one of it's members.
Very useful.
## String Helpers
String helpers are inline functions that become available when (and if) the `fio_h` file is included with the `FIO_INCLUDE_STR` macro.
This can be performed even after if the `fio.h` file was previously included. i.e.:
```c
#include // No string helpers
#define FIO_INCLUDE_STR
#include // String helpers become available
```
### String API - Initialization and Destruction
The String API is used to manage binary strings, allowing the NUL byte to be a valid byte in the middle of the string (unlike C strings)
The String API uses the type `fio_str_s`, which shouldn't be accessed directly.
The `fio_str_s` objects can be allocated either on the stack or on the heap.
However, reference counting provided by the `fio_free2` and the `fio_str_send_free2` functions, requires that the `fio_str_s` object be allocated on the heap using `fio_malloc` or `fio_str_new2`.
#### String Memory Allocation
String memory is managed by facil.io's allocation / deallocation routines (`fio_malloc`, etc').
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
A definition of `FIO_FORCE_MALLOC_TMP` doesn't persist, it will be cleared away once `fio.h` was included.
A definition of `FIO_FORCE_MALLOC` will persist for all the types defined by `fio.h` until undefined but might be defined during compilation and shouldn't be manually unset.
For example:
```c
#define FIO_INCLUDE_STR 1
#define FIO_FORCE_MALLOC_TMP 1
#include
#ifdef FIO_FORCE_MALLOC_TMP
#error "FIO_FORCE_MALLOC_TMP is removed after it's used."
#undef
```
#### `FIO_STR_INIT`
```c
#define FIO_STR_INIT ((fio_str_s){.data = NULL, .small = 1})
```
This value should be used for initialization. For example:
```c
// on the stack
fio_str_s str = FIO_STR_INIT;
// or on the heap
fio_str_s *str = fio_malloc(sizeof(*str);
*str = FIO_STR_INIT;
```
Remember to cleanup:
```c
// on the stack
fio_str_free(&str);
// or on the heap
fio_str_free(str);
fio_free(str);
```
#### `FIO_STR_INIT_EXISTING`
```c
#define FIO_STR_INIT_EXISTING(buffer, length, capacity) \
((fio_str_s){.data = (buffer), \
.len = (length), \
.capa = (capacity), \
.dealloc = fio_free})
```
This macro allows the container to be initialized with existing data, as long as it's memory was allocated using `fio_malloc`.
The `capacity` value should exclude the NUL character (if exists).
#### `FIO_STR_INIT_STATIC`
```c
#define FIO_STR_INIT_STATIC(buffer) \
((fio_str_s){.data = (buffer), .len = strlen((buffer)), .dealloc = NULL})
```
This macro allows the container to be initialized with existing static data, that shouldn't be freed.
#### `FIO_STR_INIT_STATIC2`
```c
#define FIO_STR_INIT_STATIC2(buffer, length) \
((fio_str_s){.data = (buffer), .len = (length), .dealloc = NULL})
```
This macro allows the container to be initialized with existing static data, that shouldn't be freed.
#### `fio_str_new2`
```c
inline fio_str_s *fio_str_new2(void);
```
Allocates a new fio_str_s object on the heap and initializes it.
Use `fio_str_free2` to free both the String data and the container.
**Note**: This makes the allocation and reference counting logic more intuitive.
#### `fio_str_new_copy2`
```c
inline fio_str_s *fio_str_new_copy2(fio_str_s *src);
```
Allocates a new fio_str_s object on the heap, initializes it and copies the
original (`src`) string into the new string.
Use `fio_str_free2` to free the new string's data and it's container.
#### `fio_str_dup`
```c
inline fio_str_s *fio_str_dup(fio_str_s *s);
```
Adds a references to the current String object and returns itself.
**Note**: Nothing is copied, reference Strings are referencing the same String. Editing one reference will effect the other.
The originals' String container should remain in scope (if on the stack) or remain allocated (if on the heap) until all the references were freed using `fio_str_free` / `fio_str_free2` or discarded.
#### `fio_str_free`
```c
inline int fio_str_free(fio_str_s *s);
```
Frees the String's resources and reinitializes the container.
Note: if the container isn't allocated on the stack, it should be freed
separately using `free` or `fio_free`.
Returns 0 if the data was freed and -1 if the String is NULL or has un-freed
references (see fio_str_dup).
#### `fio_str_free2`
```c
void fio_str_free2(fio_str_s *s);
```
Frees the String's resources AS WELL AS the container.
Note: the container is freed using `fio_free`, make sure `fio_malloc` was used to allocate it.
#### `fio_str_send_free2`
```c
inline ssize_t fio_str_send_free2(const intptr_t uuid,
const fio_str_s *str);
```
`fio_str_send_free2` sends the fio_str_s using `fio_write2`, freeing both the String and the container once the data was sent.
As the naming indicates, the String is assumed to have been allocated using `fio_str_new2` or `fio_malloc`.
#### `fio_str_detach`
```c
FIO_FUNC char *fio_str_detach(fio_str_s *s);
```
Returns a C string with the existing data, clearing the `fio_str_s` object's String.
Note: the String data is removed from the container, but the container isn't freed.
Returns NULL if there's no String data.
Remember to `fio_free` the returned data and - if required - `fio_str_free2` the container.
### String API - String state (data pointers, length, capacity, etc')
Many of the String state functions return a `fio_str_info_s` structure with information about the String:
```c
typedef struct {
size_t capa; /* String capacity */
size_t len; /* String length */
char *data; /* String data */
} fio_str_info_s;
```
Using this approach is safer than accessing the String data directly, since the short Strings behave differently than long strings and the `fio_str_s` structure fields are only valid for long strings.
#### `fio_str_info`
```c
inline fio_str_info_s fio_str_info(const fio_str_s *s);
```
Returns the String's complete state (capacity, length and pointer).
#### `fio_str_len`
```c
inline size_t fio_str_len(fio_str_s *s);
```
Returns the String's length in bytes.
#### `fio_str_data`
```c
inline char *fio_str_data(fio_str_s *s);
```
Returns a pointer (`char *`) to the String's content.
#### `fio_str_bytes`
```c
#define fio_str_bytes(s) ((uint8_t *)fio_str_data((s)))
```
Returns a byte pointer (`uint8_t *`) to the String's unsigned content.
#### `fio_str_capa`
```c
inline size_t fio_str_capa(fio_str_s *s);
```
Returns the String's existing capacity (total used & available memory).
#### `fio_str_resize`
```c
inline fio_str_info_s fio_str_resize(fio_str_s *s, size_t size);
```
Sets the new String size without reallocating any memory (limited by existing capacity).
Returns the updated state of the String.
Note: When shrinking, any existing data beyond the new size may be corrupted.
#### `fio_str_clear`
```c
#define fio_str_clear(s) fio_str_resize((s), 0)
```
Clears the string (retaining the existing capacity).
#### `fio_str_hash`
```c
uint64_t fio_str_hash(const fio_str_s *s);
```
Returns the String's Risky Hash (see [`fio_risky_hash`](#fio_risky_hash)).
This value is machine/instance specific (hash seed is a memory address).
NOTE: the hashing function might be changed at any time without notice. It wasn't cryptographically analyzed and safety against malicious data can't be guaranteed. Use [`fio_siphash13`](#fio_siphash13) or [`fio_siphash24`](#fio_siphash24) when hashing data from external sources.
#### `fio_risky_hash`
```c
static inline uintptr_t fio_risky_hash(char *data, size_t len, uint64_t seed);
```
Computes a facil.io [Risky Hash](https://facil.io/0.7.x/riskyhash/) - modeled after the amazing [xxHash](https://github.com/Cyan4973/xxHash) (which has a BSD license) and named "Risky Hash" because writing your own hashing function is a risky business, full of pitfalls and hours of testing...
Risky Hash passed the [SMHasher](https://github.com/rurban/smhasher) tests with wonderful results and can be safely used for hash maps when hashing safe data.
However, Risky Hash isn't as battle tested as SipHash and because of the great unknown, it should be considered "risky" and might be exposed to malicious attacks, such as [Hash Flooding Attacks](https://medium.freecodecamp.org/hash-table-attack-8e4371fc5261).
**Note**: Although this function can be used independently of the `fio_str_s` object and functions, it is only available if the `FIO_INCLUDE_STR` flag was defined.
### String API - Memory management
#### `fio_str_compact`
```c
void fio_str_compact(fio_str_s *s);
```
Performs a best attempt at minimizing memory consumption.
Actual effects depend on the underlying memory allocator and it's implementation. Not all allocators will free any memory.
#### `fio_str_capa_assert`
```c
fio_str_info_s fio_str_capa_assert(fio_str_s *s, size_t needed);
```
Requires the String to have at least `needed` capacity (including existing data).
Returns the current state of the String.
### String API - UTF-8 State
#### `fio_str_utf8_valid`
```c
size_t fio_str_utf8_valid(fio_str_s *s);
```
Returns 1 if the String is UTF-8 valid and 0 if not.
#### `fio_str_utf8_len`
```c
size_t fio_str_utf8_len(fio_str_s *s);
```
Returns the String's length in UTF-8 characters.
#### `fio_str_utf8_select`
```c
int fio_str_utf8_select(fio_str_s *s, intptr_t *pos, size_t *len);
```
Takes a UTF-8 character selection information (UTF-8 position and length) and updates the same variables so they reference the raw byte slice information.
If the String isn't UTF-8 valid up to the requested selection, than `pos` will be updated to `-1` otherwise values are always positive.
The returned `len` value may be shorter than the original if there wasn't enough data left to accommodate the requested length. When a `len` value of `0` is returned, this means that `pos` marks the end of the String.
Returns -1 on error and 0 on success.
#### `FIO_STR_UTF8_CODE_POINT`
```c
#define FIO_STR_UTF8_CODE_POINT(ptr, end, i32) // ...
```
Advances the `ptr` by one utf-8 character, placing the value of the UTF-8 character into the i32 variable (which must be a signed integer with 32bits or more). On error, `i32` will be equal to `-1` and `ptr` will not step forwards.
The `end` value is only used for overflow protection.
This helper macro is used internally but left exposed for external use.
### String API - Content Manipulation and Review
#### `fio_str_write`
```c
inline fio_str_info_s fio_str_write(fio_str_s *s, const void *src,
size_t src_len);
```
Writes data at the end of the String (similar to `fio_str_insert` with the argument `pos == -1`).
#### `fio_str_write_i`
```c
inline fio_str_info_s fio_str_write_i(fio_str_s *s, int64_t num);
```
Writes a number at the end of the String using normal base 10 notation.
#### `fio_str_concat`
```c
inline fio_str_info_s fio_str_concat(fio_str_s *dest,
fio_str_s const *src);
```
Appens the `src` String to the end of the `dest` String.
If `dest` is empty, the resulting Strings will be equal.
#### `fio_str_join`
```c
#define fio_str_join(dest, src) fio_str_concat((dest), (src))
```
Alias for [`fio_str_concat`](#fio_str_concat).
#### `fio_str_replace`
```c
fio_str_info_s fio_str_replace(fio_str_s *s, intptr_t start_pos,
size_t old_len, const void *src,
size_t src_len);
```
Replaces the data in the String - replacing `old_len` bytes starting at `start_pos`, with the data at `src` (`src_len` bytes long).
Negative `start_pos` values are calculated backwards, `-1` == end of String.
When `old_len` is zero, the function will insert the data at `start_pos`.
If `src_len == 0` than `src` will be ignored and the data marked for replacement will be erased.
#### `fio_str_vprintf`
```c
fio_str_info_s fio_str_vprintf(fio_str_s *s, const char *format,
va_list argv);
```
Writes to the String using a vprintf like interface.
Data is written to the end of the String.
#### `fio_str_printf`
```c
fio_str_info_s fio_str_printf(fio_str_s *s, const char *format, ...);
```
Writes to the String using a printf like interface.
Data is written to the end of the String.
#### `fio_str_readfile`
```c
fio_str_info_s fio_str_readfile(fio_str_s *s, const char *filename,
intptr_t start_at, intptr_t limit);
```
Opens the file `filename` and pastes it's contents (or a slice ot it) at the
end of the String. If `limit == 0`, than the data will be read until EOF.
If the file can't be located, opened or read, or if `start_at` is beyond
the EOF position, NULL is returned in the state's `data` field.
Works on POSIX only.
#### `fio_str_freeze`
```c
inline void fio_str_freeze(fio_str_s *s);
```
Prevents further manipulations to the String's content.
#### `fio_str_iseq`
```c
inline int fio_str_iseq(const fio_str_s *str1, const fio_str_s *str2);
```
Binary comparison returns `1` if both strings are equal and `0` if not.
## Dynamic Arrays
The `fio.h` header includes a simple typed dynamic array with a minimal API that can be adapted to any type.
**Note**: [The API for the `FIOBJ` Array is located here](https://facil.io/0.7.x/fiobj_ary/). This is the documentation for the core library Array.
To create an Array type, define the macro FIO_ARY_NAME. i.e.:
```c
#define FIO_ARY_NAME fio_cstr_ary
#define FIO_ARY_TYPE char *
#define FIO_ARY_COMPARE(k1, k2) (!strcmp((k1), (k2)))
#include
```
It's possible to create a number of Set or Array types by re-including the `fio.h` header. i.e.:
```c
#define FIO_INCLUDE_STR
#include // adds the fio_str_s types and functions
#define FIO_ARY_NAME fio_int_ary
#define FIO_ARY_TYPE int
#include // creates the fio_int_ary_s Array and functions
#define FIO_ARY_NAME fio_str_ary
#define FIO_ARY_TYPE fio_str_s *
#define FIO_ARY_COMPARE(k1, k2) (fio_str_iseq((k1), (k2)))
#define FIO_ARY_COPY(key) fio_str_dup((key))
#define FIO_ARY_DESTROY(key) fio_str_free2((key))
#include // creates the fio_str_ary_s Array and functions
```
### Array Memory allocation
The Array's memory is managed by facil.io's allocation / deallocation routines (`fio_malloc`, etc').
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
A definition of `FIO_FORCE_MALLOC_TMP` doesn't persist, it will be cleared away once `fio.h` was included.
A definition of `FIO_FORCE_MALLOC` will persist for all the types defined by `fio.h` until undefined but might be defined during compilation and shouldn't be manually unset.
For example:
```c
#define FIO_FORCE_MALLOC_TMP 1
#define FIO_ARY_NAME fio_int_ary
#define FIO_ARY_TYPE int
#include // creates the fio_int_ary_s Array and functions
#ifdef FIO_FORCE_MALLOC_TMP
#error "FIO_FORCE_MALLOC_TMP is removed after it's used."
#undef
```
More flexibility (such as using a third memory allocation scheme) can be achieved using the `FIO_ARRAY_MALLOC` type macros, as detailed below.
### Defining the Array
#### `FIO_ARY_NAME`
The `FIO_ARY_NAME` is required and will be used to set a for the Array's type and functions.
The Array type will be `FIO_ARY_NAME_s` and each function will be translated to `FIO_ARY_NAME_func`, where `FIO_ARY_NAME` is replaced by the macro set.
For example:
```c
#define FIO_ARY_NAME fio_cstr_ary
#define FIO_ARY_TYPE char *
#define FIO_ARY_COMPARE(k1, k2) (!strcmp((k1), (k2)))
#include
fio_cstr_ary_s global_array = FIO_ARY_INIT;
void populate_array(char ** data, int len) {
for (int i = 0; i < len; ++i)
{
fio_cstr_ary_push(&global_array, data[i]);
}
}
```
#### `FIO_ARY_TYPE`
```c
#if !defined(FIO_ARY_TYPE)
#define FIO_ARY_TYPE void *
#endif
```
The default Array object type is `void *` */
#### `FIO_ARY_INVALID`
```c
#if !defined(FIO_ARY_INVALID)
static FIO_ARY_TYPE const FIO_NAME(s___const_invalid_object);
#define FIO_ARY_INVALID FIO_NAME(s___const_invalid_object)
#endif
```
The `FIO_ARY_INVALID` value should be an object with all bytes set to 0. Since `FIO_ARY_TYPE` type is unknown, a constant static object is created. However, it is better to manually define `FIO_ARY_INVALID`.
#### `FIO_ARY_COMPARE`
```c
#if !defined(FIO_ARY_COMPARE)
#define FIO_ARY_COMPARE(o1, o2) ((o1) == (o2))
#endif
```
The default object comparison assumes a simple type.
#### `FIO_ARY_COPY`
```c
#ifndef FIO_ARY_COPY
#define FIO_ARY_COPY(dest, obj) ((dest) = (obj))
#endif
```
The default object copy is a simple assignment.
#### `FIO_ARY_DESTROY`
```c
#ifndef FIO_ARY_DESTROY
#define FIO_ARY_DESTROY(obj) ((void)0)
#endif
```
The default object destruction is a no-op.
**Note**: Before freeing the Array, `FIO_ARY_DESTROY` will be automatically called for every existing object, including any invalid objects (if any). It is important that the `FIO_ARY_DESTROY` macro allows for invalid data (all bytes are `0`).
#### `FIO_ARY_MALLOC`
```c
#ifndef FIO_ARY_MALLOC
#define FIO_ARY_MALLOC(size) fio_malloc((size))
#endif
```
The default Array allocator is set to `fio_malloc`.
It's important to note that the default allocator **must** set all the allocated bytes to zero, exactly as `fio_malloc` does.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
#### `FIO_ARY_REALLOC`
```c
#ifndef FIO_ARY_REALLOC /* NULL ptr indicates new allocation */
#define FIO_ARY_REALLOC(ptr, original_size, new_size, valid_data_length) \
fio_realloc2((ptr), (new_size), (valid_data_length))
#endif
```
The default Array re-allocator is set to `fio_realloc2`. All bytes will be set to zero except the copied data and - in some cases, where copy alignment error occurs - the last 16 bytes.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
#### `FIO_ARY_DEALLOC`
```c
#ifndef FIO_ARY_DEALLOC
#define FIO_ARY_DEALLOC(ptr, size) fio_free((ptr))
#endif
```
The default Array deallocator is set to `fio_free`.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
### Naming the Array
Because the type and function names are dictated by the `FIO_ARY_NAME`, it's impossible to name the functions and types that will be created.
For the purpose of this documentation, the `FIO_ARY_NAME(name)` will mark a type / function name so that it's equal to `FIO_ARY_NAME + "_" + name`.
i.e., if `FIO_SET_NAME` is `foo` than `FIO_SET_NAME(s)` is `foo_s`.
### Array Initialization and State
#### `FIO_ARY_NAME(s)`
```c
typedef struct FIO_ARY_NAME(s) FIO_ARY_NAME(s);
struct FIO_NAME(s) {
size_t start; /* first index where data was already written */
size_t end; /* next spot to write at tail */
size_t capa; /* existing capacity */
FIO_ARY_TYPE *arry; /* the actual array's memory, if any */
};
```
The Array container type. It is advised that the container be considered opaque and that the functions provided are used to access it's data (instead of direct data access).
#### `FIO_ARY_INIT`
```c
#ifndef FIO_ARY_INIT
#define FIO_ARY_INIT \
{ .capa = 0 }
#endif
```
Initializes the Array.
#### `FIO_ARY_NAME(free)`
```c
FIO_FUNC inline void FIO_ARY_NAME(free)(FIO_ARY_NAME(s) * ary);
```
Frees the array's internal data.
#### `FIO_ARY_NAME(count)`
```c
FIO_FUNC inline size_t FIO_ARY_NAME(count)(FIO_ARY_NAME(s) * ary);
```
Returns the number of elements in the Array.
#### `FIO_ARY_NAME(capa)`
```c
FIO_FUNC inline size_t FIO_ARY_NAME(capa)(FIO_ARY_NAME(s) * ary);
```
Returns the current, temporary, array capacity (it's dynamic).
---
### Array data management
#### `FIO_ARY_NAME(concat)`
```c
FIO_FUNC inline void FIO_ARY_NAME(concat)(FIO_ARY_NAME(s) * dest, FIO_ARY_NAME(s) * src);
```
Adds all the items in the `src` Array to the end of the `dest` Array.
The `src` Array remain untouched.
#### `FIO_ARY_NAME(set)`
```c
FIO_FUNC inline void FIO_ARY_NAME(set)(FIO_ARY_NAME(s) * ary, intptr_t index,
FIO_ARY_TYPE data, FIO_ARY_TYPE *old);
```
Sets `index` to the value in `data`.
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
If `old` isn't NULL, the existing data will be copied to the location pointed
to by `old` before the copy in the Array is destroyed.
#### `FIO_ARY_NAME(get)`
```c
FIO_FUNC inline FIO_ARY_TYPE FIO_ARY_NAME(get)(FIO_ARY_NAME(s) * ary, intptr_t index);
```
Returns the value located at `index` (no copying is peformed).
If `index` is negative, it will be counted from the end of the Array (-1 ==
last element).
#### `FIO_ARY_NAME(find)`
```c
FIO_FUNC inline intptr_t FIO_ARY_NAME(find)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE data);
```
Returns the index of the object or -1 if the object wasn't found.
#### `FIO_ARY_NAME(remove)`
```c
FIO_FUNC inline int FIO_ARY_NAME(remove)(FIO_ARY_NAME(s) * ary, intptr_t index,
FIO_ARY_TYPE *old);
```
Removes an object from the array, **moving** all the other objects to prevent
"holes" in the data.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns 0 on success and -1 on error.
#### `FIO_ARY_NAME(remove2)`
```c
FIO_FUNC inline int FIO_ARY_NAME(remove2)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE data,
FIO_ARY_TYPE *old);
```
Removes an object from the array, if it exists, MOVING all the other objects
to prevent "holes" in the data.
Returns -1 if the object wasn't found or 0 if the object was successfully
removed.
#### `FIO_ARY_NAME(to_a)`
```c
FIO_FUNC inline FIO_ARY_TYPE *FIO_ARY_NAME(to_a)(FIO_ARY_NAME(s) * ary);
```
Returns a pointer to the C array containing the objects.
#### `FIO_ARY_NAME(push)`
```c
FIO_FUNC inline int FIO_ARY_NAME(push)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE data);
```
Pushes an object to the end of the Array. Returns -1 on error.
#### `FIO_ARY_NAME(pop)`
```c
FIO_FUNC inline int FIO_ARY_NAME(pop)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE *old);
```
Removes an object from the end of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
#### `FIO_ARY_NAME(unshift)`
```c
FIO_FUNC inline int FIO_ARY_NAME(unshift)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE data);
```
Unshifts an object to the beginning of the Array. Returns -1 on error.
This could be expensive, causing `memmove`.
#### `FIO_ARY_NAME(shift)`
```c
FIO_FUNC inline int FIO_ARY_NAME(shift)(FIO_ARY_NAME(s) * ary, FIO_ARY_TYPE *old);
```
Removes an object from the beginning of the Array.
If `old` is set, the data is copied to the location pointed to by `old`
before the data in the array is destroyed.
Returns -1 on error (Array is empty) and 0 on success.
#### `FIO_ARY_NAME(each)`
```c
FIO_FUNC inline size_t FIO_ARY_NAME(each)(FIO_ARY_NAME(s) * ary, size_t start_at,
int (*task)(FIO_ARY_TYPE pt, void *arg),
void *arg);
```
Iteration using a callback for each entry in the array.
The callback task function must accept an the entry data as well as an opaque
user pointer.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
#### `FIO_ARY_NAME(compact)`
```c
FIO_FUNC inline void FIO_ARY_NAME(compact)(FIO_ARY_NAME(s) * ary);
```
Removes any FIO_ARY_TYPE_INVALID object from an Array (NULL pointers by
default), keeping all other data in the array.
This action is O(n) where n in the length of the array.
It could get expensive.
#### `FIO_ARY_FOR(ary, pos)`
```c
#define FIO_ARY_FOR(ary, pos)
```
Iterates through the list using a `for` loop.
Access the object with the pointer `pos`. The `pos` variable can be named
however you please.
Avoid editing the array during a FOR loop, although I hope it's possible, I
wouldn't count on it.
## Hash Maps / Sets
facil.io includes a simple ordered Hash Map / Set implementation, with a minimal API.
**Note**: [The API for the `FIOBJ` Hash Map is located here](https://facil.io/0.7.x/fiobj_hash/). This is the documentation for the core library Hash Map / Set.
A Set is basically a Hash Map where the keys are also the values, it's often used for caching objects while a Hash Map is used to find one object using another.
A Hash Map is basically a set where the objects in the Set are key-value couplets and only the keys are tested when searching the Set.
The Set's object type and behavior is controlled by the FIO_SET_OBJ_* marcos.
To create a Set or a Hash Map, the macro FIO_SET_NAME must be defined. i.e.:
```c
#define FIO_SET_NAME fio_str_info_set
#define FIO_SET_OBJ_TYPE char *
#define FIO_SET_OBJ_COMPARE(k1, k2) (!strcmp((k1), (k2)))
#include
// ...
fio_str_info_set_s my_set = FIO_SET_INIT; // note type name matches FIO_SET_NAME
uint64_t hash = fio_siphash("foo", 3);
fio_str_info_set_insert(&my_set, hash, "foo"); // note function name
```
This can be performed a number of times, defining a different Set / Hash Map each time.
### Security Concerns
Since the facio.io hash map implementation allows any hash function to be used, there are a number of important safety considerations developers should be aware of.
Hash Maps and Sets could be attacked using "hash flooding" attacks.
The weaker the hashing function, the more likely it is to achieve a hash flooding attack.
To avoid these attacks, there are three requirements:
1. The hashing function, used to produce the digest (hash value) **should** allow resistance to hash flooding attacks. This means:
1. The function should require a "seed" / "secret".
1. Malicious data can't be authored to circumvent the "secret".
1. The implementation uses the secret to randomize digest values.
A bad secret will be a function pointer or a value set during startup.
A good secret will be hash table specific, so different hash tables have different random secrets. For example, hashing the table's memory address with a good hash function would be a good secret (assuming the address remains the same while the table is in use).
1. The hash map implementation **must** use as many bits from the hashed value, or rotate the bits that are in use.
In general, hash maps / sets are either prime number based (use more bits from the hash value but take longer for lookups) or modulus 2 based (faster lookup times).
Modulus 2 based hash map implementations are very common, but they could be exposed to lower bit collision attacks, where a partial collision is enough to attack the hash table.
This risk should be mitigated by utilizing as many bits from the digest as possible.
For example, facil.io is modulus 2 based, but it uses different bits every time it grows, protecting against lower bit collision attacks.
1. The implementation **must** react to attacks that circumvent the hash functions.
A good hash function should be considered the first line of defense for a hash map implementation, but it should **not** be the main line of defense.
A hash map implementation should, in addition to mitigating partial and full collisions using cuckoo steps / round robin, bit mangling, etc', recognize (full) collision attacks and prevent hash table growth when too many full collisions occur.
For example, facil.io hash maps and sets will recognize an attack when 96 full collisions are detected (defined as `FIO_SET_MAX_MAP_FULL_COLLISIONS` in `fio.h`). After this point, new colliding data will overwrite old colliding data.
### Hash Map / Set Memory allocation
The Hash Map's memory is managed by facil.io's allocation / deallocation routines (`fio_malloc`, etc').
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
A definition of `FIO_FORCE_MALLOC_TMP` doesn't persist, it will be cleared away once `fio.h` was included.
A definition of `FIO_FORCE_MALLOC` will persist for all the types defined by `fio.h` until undefined but might be defined during compilation and shouldn't be manually unset.
For example:
```c
#define FIO_FORCE_MALLOC_TMP 1
#define FIO_SET_NAME fio_str_info_set
#define FIO_SET_OBJ_TYPE char *
#define FIO_SET_OBJ_COMPARE(k1, k2) (!strcmp((k1), (k2)))
#include // created the fio_str_info_set_s and functions
#ifdef FIO_FORCE_MALLOC_TMP
#error "FIO_FORCE_MALLOC_TMP is removed after it's used."
#undef
```
More flexibility (such as using a third memory allocation scheme) can be achieved using the `FIO_SET_MALLOC` type macros, as detailed below.
### Defining the Set / Hash Map
The Set's object type and behavior is controlled by the FIO_SET_OBJ_* marcos: `FIO_SET_OBJ_TYPE`, `FIO_SET_OBJ_COMPARE`, `FIO_SET_OBJ_COPY`, `FIO_SET_OBJ_DESTROY`. i.e.:
```c
#define FIO_INCLUDE_STR
#include // adds the fio_str_s types and functions
#define FIO_SET_NAME fio_str_set
#define FIO_SET_OBJ_TYPE fio_str_s *
#define FIO_SET_OBJ_COMPARE(s1, s2) fio_str_iseq((s1), (s2))
#define FIO_SET_OBJ_COPY(dest, src) (dest) = fio_str_new_copy2((src))
#define FIO_SET_OBJ_DESTROY(str) fio_str_free2((str))
#include // creates the fio_str_set_s Set and functions
int main(void) {
fio_str_s tmp = FIO_STR_INIT_STATIC("foo");
// Initialize a Set for String caching
fio_str_set_s my_set = FIO_SET_INIT;
// Insert object to Set (will make copy)
fio_str_s *cpy = fio_str_set_insert(&my_set, fio_str_hash(&tmp), &tmp);
printf("Original data %p\n", (void *)&tmp);
printf("Stored data %p\n", (void *)cpy);
printf("Stored data content: %s\n", fio_str_data(cpy));
// Free set (will free copy).
fio_str_set_free(&my_set);
}
```
To create a Hash Map, rather than a pure Set, the macro FIO_SET_KET_TYPE must
be defined. i.e.:
```c
#define FIO_SET_KEY_TYPE char *
```
This allows the FIO_SET_KEY_* macros to be defined as well. For example:
```c
#define FIO_SET_NAME fio_str_hash
#define FIO_SET_KEY_TYPE char *
#define FIO_SET_KEY_COMPARE(k1, k2) (!strcmp((k1), (k2)))
#define FIO_SET_OBJ_TYPE char *
#include
```
#### `FIO_SET_OBJ_TYPE`
```c
// default:
#define FIO_SET_OBJ_TYPE void *
```
Set's a Set or a Hash Map's object type. Defaults to `void *`.
#### `FIO_SET_OBJ_COMPARE`
```c
// default:
#define FIO_SET_OBJ_COMPARE(o1, o2) (1)
```
Compares two Set objects. This is only relevant to pure Sets (not Hash Maps). Defaults to doing nothing (always true).
#### `FIO_SET_OBJ_COPY`
```c
// default:
#define FIO_SET_OBJ_COPY(dest, obj) ((dest) = (obj))
```
Copies an object's data from an external object to the internal storage. This allows String and other dynamic data to be copied and retained for the lifetime of the object.
#### `FIO_SET_OBJ_DESTROY`
```c
// default:
#define FIO_SET_OBJ_DESTROY(obj) ((void)0)
```
Frees any allocated memory / resources used by the object data. By default this does nothing.
#### `FIO_SET_KEY_TYPE`
```c
#undef FIO_SET_KEY_TYPE
```
By defining a key type, the Set will be converted to a Hash Map and it's API will be slightly altered to reflect this change.
By default, no key type is defined.
#### `FIO_SET_KEY_COMPARE`
```c
#define FIO_SET_KEY_COMPARE(key1, key2) ((key1) == (key2))
```
Compares two Hash Map keys.
This is only relevant if the `FIO_SET_KEY_TYPE` was defined.
#### `FIO_SET_KEY_COPY`
```c
#define FIO_SET_KEY_COPY(dest, key) ((dest) = (key))
```
Copies the key's data from an external key to the internal storage. This allows String and other dynamic data to be copied and retained for the lifetime of the key-value pair.
This is only relevant if the `FIO_SET_KEY_TYPE` was defined.
#### `FIO_SET_KEY_DESTROY`
```c
#define FIO_SET_KEY_DESTROY(key) ((void)0)
```
Frees any allocated memory / resources used by the key data. By default this does nothing.
This is only relevant if the `FIO_SET_KEY_TYPE` was defined.
#### `FIO_SET_REALLOC`
```c
#define FIO_SET_REALLOC(ptr, original_size, new_size, valid_data_length) \
realloc((ptr), (new_size))
```
Allows for custom memory allocation / deallocation routines.
The default allocator is facil.io's `fio_realloc`.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
#### `FIO_SET_CALLOC`
```c
#define FIO_SET_CALLOC(size, count) calloc((size), (count))
```
Allows for custom memory allocation / deallocation routines.
The default allocator is facil.io's `fio_calloc`.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
#### `FIO_SET_FREE`
```c
#define FIO_SET_FREE(ptr, size) free((ptr))
```
Allows for custom memory allocation / deallocation routines.
The default deallocator is facil.io's `fio_free`.
To use the system's memory allocation / deallocation define `FIO_FORCE_MALLOC_TMP` as `1` before including `fio.h`.
### Naming the Set / Hash Map
Because the type and function names are dictated by the `FIO_SET_NAME`, it's impossible to name the functions and types that will be created.
For the purpose of this documentation, the `FIO_SET_NAME(name)` will mark a type / function name so that it's equal to `FIO_SET_NAME + "_" + name`.
i.e., if `FIO_SET_NAME == foo` than `FIO_SET_NAME(s) == foo_s`.
### Set / Hash Map Initialization
#### `FIO_SET_NAME(s)`
The Set's / Hash Map's type name. It's content should be considered opaque.
#### `FIO_SET_INIT`
```c
#define FIO_SET_INIT { .capa = 0 }
```
Initializes the Set or the Hash Map.
#### `FIO_SET_NAME(free)`
```c
void FIO_SET_NAME(free)(FIO_SET_NAME(s) * set);
```
Frees all the objects in the Hash Map / Set and deallocates any internal resources.
### Hash Map Find / Insert
These functions are defined if the Set defined is a Hash Map (`FIO_SET_KEY_TYPE` was defined).
#### `FIO_SET_NAME(find)` (Hash Map)
```c
inline FIO_SET_OBJ_TYPE
FIO_SET_NAME(find)(FIO_SET_NAME(s) * set, const FIO_SET_HASH_TYPE hash_value,
FIO_SET_KEY_TYPE key);
```
Locates an object in the Hash Map, if it exists.
**Note**: This is the function's Hash Map variant. See `FIO_SET_KEY_TYPE`.
#### `FIO_SET_NAME(insert)` (Hash Map)
```c
inline void FIO_SET_NAME(insert)(FIO_SET_NAME(s) * set,
const FIO_SET_HASH_TYPE hash_value,
FIO_SET_KEY_TYPE key,
FIO_SET_OBJ_TYPE obj,
FIO_SET_OBJ_TYPE *old);
```
Inserts an object to the Hash Map, rehashing if required, returning the new object's location using a pointer.
If an object already exists in the Hash Map, it will be destroyed.
If `old` isn't NULL, the existing object (if any) will be copied to the location pointed to by `old` before it is destroyed.
**Note**: This is the function's Hash Map variant. See `FIO_SET_KEY_TYPE`.
#### `FIO_SET_NAME(remove)` (Hash Map)
```c
inline int FIO_SET_NAME(remove)(FIO_SET_NAME(s) * set,
const FIO_SET_HASH_TYPE hash_value,
FIO_SET_KEY_TYPE key
FIO_SET_OBJ_TYPE *old);
```
Removes an object from the Set, rehashing if required.
Returns 0 on success and -1 if the object wasn't found.
If `old` isn't `NULL`, than the existing object (if any) would be copied to the location pointed to by `old`.
**Note**: This is the function's Hash Map variant. See `FIO_SET_KEY_TYPE`.
### Set Find / Insert
These functions are defined if the Set is a pure Set (not a Hash Map).
#### `FIO_SET_NAME(find)` (Set)
```c
inline FIO_SET_OBJ_TYPE
FIO_SET_NAME(find)(FIO_SET_NAME(s) * set, const FIO_SET_HASH_TYPE hash_value,
FIO_SET_OBJ_TYPE obj);
```
Locates an object in the Set, if it exists.
**Note**: This is the function's pure Set variant (no `FIO_SET_KEY_TYPE`).
#### `FIO_SET_NAME(insert)` (Set)
```c
inline FIO_SET_OBJ_TYPE
FIO_SET_NAME(insert)(FIO_SET_NAME(s) * set, const FIO_SET_HASH_TYPE hash_value,
FIO_SET_OBJ_TYPE obj);
```
Inserts an object to the Set only if it's missing, rehashing if required, returning the new (or old) object's pointer.
If the object already exists in the set, than the new object will be destroyed and the old object will be returned.
**Note**: This is the function's pure Set variant (no `FIO_SET_KEY_TYPE`).
#### `FIO_SET_NAME(overwrite)` (Set)
```c
inline FIO_SET_OBJ_TYPE FIO_SET_NAME(overwrite)(FIO_SET_NAME(s) * set,
const FIO_SET_HASH_TYPE hash_value,
FIO_SET_OBJ_TYPE obj,
FIO_SET_OBJ_TYPE *old);
```
Inserts an object to the Set only overwriting any existing data, rehashing if required.
If `old` is set, the old object (if any) will be copied to the location pointed to by `old`.
**Note**: This function doesn't exist when `FIO_SET_KEY_TYPE` is defined.
#### `FIO_SET_NAME(remove)` (Set)
```c
inline int FIO_SET_NAME(remove)(FIO_SET_NAME(s) * set,
const FIO_SET_HASH_TYPE hash_value,
FIO_SET_OBJ_TYPE obj,
FIO_SET_OBJ_TYPE *old);
```
Removes an object from the Set, rehashing if required.
Returns 0 on success and -1 if the object wasn't found.
If `old` is set, the old object (if any) will be copied to the location pointed to by `old`.
**Note**: This is the function's pure Set variant (no `FIO_SET_KEY_TYPE`).
### Set / Hash Map Data
#### `FIO_SET_NAME(last)`
```c
inline FIO_SET_TYPE FIO_SET_NAME(last)(FIO_SET_NAME(s) * set);
```
Allows a peak at the Set's last element.
Remember that objects might be destroyed if the Set is altered (`FIO_SET_OBJ_DESTROY` / `FIO_SET_KEY_DESTROY`).
#### `FIO_SET_NAME(pop)`
```c
inline void FIO_SET_NAME(pop)(FIO_SET_NAME(s) * set);
```
Allows the Hash to be momentarily used as a stack, destroying the last object added (`FIO_SET_OBJ_DESTROY` / `FIO_SET_KEY_DESTROY`).
#### `FIO_SET_NAME(count)`
```c
inline size_t FIO_SET_NAME(count)(const FIO_SET_NAME(s) * set);
```
Returns the number of object currently in the Set.
#### `FIO_SET_NAME(capa)`
```c
inline size_t FIO_SET_NAME(capa)(const FIO_SET_NAME(s) * set);
```
Returns a temporary theoretical Set capacity.
This could be used for testing performance and memory consumption.
#### `FIO_SET_NAME(capa_require)`
```c
inline size_t FIO_SET_NAME(capa_require)(FIO_SET_NAME(s) * set,
size_t min_capa);
```
Requires that a Set contains the minimal requested theoretical capacity.
Returns the actual (temporary) theoretical capacity.
#### `FIO_SET_NAME(is_fragmented)`
```c
inline size_t FIO_SET_NAME(is_fragmented)(const FIO_SET_NAME(s) * set);
```
Returns non-zero if the Set is fragmented (more than 50% holes).
#### `FIO_SET_NAME(compact)`
```c
inline size_t FIO_SET_NAME(compact)(FIO_SET_NAME(s) * set);
```
Attempts to minimize memory usage by removing empty spaces caused by deleted items and rehashing the Set.
Returns the updated Set capacity.
#### `FIO_SET_NAME(rehash)`
```c
void FIO_SET_NAME(rehash)(FIO_SET_NAME(s) * set);
```
Forces a rehashing of the Set.
#### `FIO_SET_FOR_LOOP`
```c
#define FIO_SET_FOR_LOOP(set, pos) // ...
```
A macro for a `for` loop that iterates over all the Set's objects (in order).
`set` is a pointer to the Set variable and `pos` is a temporary variable name to be created for iteration.
`pos->hash` is the hashing value.
For Hash Maps, `pos->obj` is a key / value couplet, requiring a selection of `pos->obj.key` / `pos->obj.obj`.
For Pure Sets. the `pos->obj` is the actual object data.
**Important**: Since the Set might have "holes" (objects that were removed), it is important to skip any `pos->hash == 0` or the equivalent of `FIO_SET_HASH_COMPARE(pos->hash, FIO_SET_HASH_INVALID)`.
## General Helpers
Network applications require many common tasks, such as Atomic operations, locks, string to number conversions etc'
Many of the helper functions used by the facil.io core library are exposed for client use.
### Atomic operations
#### `fio_atomic_xchange`
```c
#define fio_atomic_xchange(p_obj, value) /* compiler specific */
```
An atomic exchange operation, sets a new value and returns the previous one.
`p_obj` must be a pointer to the object (not the object itself).
`value` is the new value to be set.
#### `fio_atomic_add`
```c
#define fio_atomic_add(p_obj, value) /* compiler specific */
```
An atomic addition operation.
Returns the new value.
`p_obj` must be a pointer to the object (not the object itself).
`value` is the new value to be set.
#### `fio_atomic_sub`
```c
#define fio_atomic_sub(p_obj, value) /* compiler specific */
```
An atomic subtraction operation.
Returns the new value.
`p_obj` must be a pointer to the object (not the object itself).
`value` is the new value to be set.
### Atomic locks
Atomic locks the `fio_lock_i` type.
```c
typedef uint8_t volatile fio_lock_i;
#define FIO_LOCK_INIT 0
```
#### `fio_trylock`
```c
inline int fio_trylock(fio_lock_i *lock);
```
Returns 0 if the lock was acquired and non-zero on failure.
#### `fio_lock`
```c
inline void fio_lock(fio_lock_i *lock);
```
Busy waits for the lock - CAREFUL, `fio_trylock` should be preferred.
#### `fio_is_locked`
```c
inline int fio_is_locked(fio_lock_i *lock);
```
Returns the current lock's state (non 0 == Busy).
#### `fio_unlock`
```c
inline int fio_unlock(fio_lock_i *lock);
```
Releases a lock. Returns non-zero if the lock was previously locked.
**Note**: Releasing an un-acquired will break the lock and could cause it's protection to fail. Make sure to only release the lock if it was previously acquired by the same "owner".
#### `fio_reschedule_thread`
```c
inline void fio_reschedule_thread(void);
```
Reschedules a thread (used as part of the `fio_lock` busy wait logic).
Implemented using `nanosleep`, which seems to be the most effective and efficient thread rescheduler.
#### `fio_throttle_thread`
```c
inline void fio_throttle_thread(size_t nano_sec);
```
A blocking throttle using `nanosleep`.
### Byte Ordering Helpers (Network vs. Local)
Different byte ordering schemes often effect network applications, especially when sending binary data.
The 16 bit number might be represented as `0xaf00` on one machine and as `0x00af` on another.
These helpers help concert between network byte ordering (Big Endian) to a local byte ordering scheme.
These helpers also help extract numerical content from a binary string.
#### `fio_lton16`
```c
#define fio_lton16(i) /* system specific */
```
Local byte order to Network byte order, 16 bit integer.
#### `fio_lton32`
```c
#define fio_lton32(i) /* system specific */
```
Local byte order to Network byte order, 32 bit integer.
#### `fio_lton64`
```c
#define fio_lton64(i) /* system specific */
```
Local byte order to Network byte order, 62 bit integer.
#### `fio_str2u16`
```c
#define fio_str2u16(c) /* system specific */
```
Reads a 16 bit number from an unaligned network ordered byte stream.
Using this function makes it easy to avoid unaligned memory access issues.
#### `fio_str2u32`
```c
#define fio_str2u32(c) /* system specific */
```
Reads a 32 bit number from an unaligned network ordered byte stream.
Using this function makes it easy to avoid unaligned memory access issues.
#### `fio_str2u64`
```c
#define fio_str2u64(c) /* system specific */
```
Reads a 64 bit number from an unaligned network ordered byte stream.
Using this function makes it easy to avoid unaligned memory access issues.
#### `fio_u2str16`
```c
#define fio_u2str16(buffer, i) /* simple byte value assignment using network order */
```
Writes a local 16 bit number to an unaligned buffer in network order.
No error checks or buffer tests are performed - make sure the buffer has at least 2 bytes available.
#### `fio_u2str32`
```c
#define fio_u2str32(buffer, i) /* simple byte value assignment using network order */
```
Writes a local 32 bit number to an unaligned buffer in network order.
No error checks or buffer tests are performed - make sure the buffer has at least 4 bytes available.
#### `fio_u2str64`
```c
#define fio_u2str64(buffer, i) /* simple byte value assignment using network order */
```
Writes a local 64 bit number to an unaligned buffer in network order.
No error checks or buffer tests are performed - make sure the buffer has at least 8 bytes available.
#### `fio_ntol16`
```c
#define fio_ntol16(i) /* system specific */
```
Network byte order to Local byte order, 16 bit integer.
#### `fio_ntol32`
```c
#define fio_ntol32(i) /* system specific */
```
Network byte order to Local byte order, 32 bit integer.
#### `fio_ntol64`
```c
#define fio_ntol64(i) /* system specific */
```
Network byte order to Local byte order, 62 bit integer.
#### `fio_bswap16`
```c
#define fio_bswap16(i) /* system specific */
```
Swaps the byte order in a 16 bit integer.
#### `fio_bswap32`
```c
#define fio_bswap32(i) /* system specific */
```
Swaps the byte order in a 32 bit integer.
#### `fio_bswap64`
```c
#define fio_bswap64(i) /* system specific */
```
Swaps the byte order in a 64 bit integer.
### Strings to Numbers
#### `fio_atol`
```c
int64_t fio_atol(char **pstr);
```
Converts between String data to a signed `int64_t`.
Numbers are assumed to be in base 10.
Octal (`0###`), Hex (`0x##`/`x##`) and binary (`0b##`/ `b##`) are recognized as well. For binary Most Significant Bit must come first.
The most significant difference between this function and `strtol` (aside of API design and speed), is the added support for binary representations.
#### `fio_atof`
```c
double fio_atof(char **pstr);
```
A helper function that converts between String data to a signed double.
Implemented using the standard `strtold` library function.
### Numbers to Strings
#### `fio_ltoa`
```c
size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
```
A helper function that writes a signed int64_t to a string.
No overflow guard is provided, make sure there's at least 68 bytes available (for base 2).
Offers special support for base 2 (binary), base 8 (octal), base 10 and base 16 (hex). An unsupported base will silently default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the beginning of the string).
Returns the number of bytes actually written (excluding the NUL terminator).
#### `fio_ftoa`
```c
size_t fio_ftoa(char *dest, double num, uint8_t base);
```
A helper function that converts between a double to a string.
No overflow guard is provided, make sure there's at least 130 bytes
available (for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL
terminator).
### Random data Generation
#### `fio_rand64`
```c
uint64_t fio_rand64(void);
```
Returns 64 psedo-random bits. Probably not cryptographically safe.
#### `fio_rand_bytes`
```c
void fio_rand_bytes(void *target, size_t length);
```
Writes `length` bytes of psedo-random bits to the target buffer. Probably not cryptographically safe.
### Base64
#### `fio_base64_encode`
```c
int fio_base64_encode(char *target, const char *data, int len);
```
This will encode a byte array (data) of a specified length and place the encoded data into the target byte buffer (target). The target buffer MUST have enough room for the expected data.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if the raw data's length isn't devisable by 3.
Always assume the target buffer should have room enough for (len*4/3 + 4) bytes.
Returns the number of bytes actually written to the target buffer (including the Base64 required padding and excluding a NULL terminator).
A NULL terminator char is NOT written to the target buffer.
#### `fio_base64url_encode`
```c
int fio_base64url_encode(char *target, const char *data, int len);
```
Same as [`fio_base64_encode`](#fio_base64_encode), but using Base64URL encoding.
#### `fio_base64_decode`
```c
int fio_base64_decode(char *target, char *encoded, int base64_len);
```
This will decode a Base64 encoded string of a specified length (len) and place the decoded data into the target byte buffer (target).
The target buffer MUST have enough room for 2 bytes in addition to the expected data (NUL byte + padding test).
A NUL byte will be appended to the target buffer. The function will return the number of bytes written to the target buffer (excluding the NUL byte).
If the target buffer is NUL, the encoded string will be destructively edited and the decoded data will be placed in the original string's buffer.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if the raw data's length isn't devisable by 3. Hence, the target buffer should be, at least, `base64_len/4*3 + 3` long.
Returns the number of bytes actually written to the target buffer (excluding the NUL terminator byte).
**Note**:
The decoder is variation agnostic (will decode Base64, Base64 URL and Base64 XML variations) and will attempt it's best to ignore invalid data, (in order to support the MIME Base64 variation in RFC 2045).
This comes at the cost of error checking, so the encoding isn't validated and invalid input might produce surprising results.
### SipHash
#### `fio_siphash24`
```c
uint64_t fio_siphash24(const void *data, size_t len);
```
A SipHash variation (2-4).
#### `fio_siphash13`
```c
uint64_t fio_siphash13(const void *data, size_t len);
```
A SipHash 1-3 variation.
#### `fio_siphash`
```c
#define fio_siphash(data, length) fio_siphash13((data), (length))
```
The Hashing function used by dynamic facil.io objects.
Currently implemented using SipHash 1-3.
### SHA-1
SHA-1 example:
```c
fio_sha1_s sha1;
fio_sha1_init(&sha1);
fio_sha1_write(&sha1,
"The quick brown fox jumps over the lazy dog", 43);
char *hashed_result = fio_sha1_result(&sha1);
```
#### `fio_sha1_init`
```c
fio_sha1_s fio_sha1_init(void);
```
Initializes or resets the `fio_sha1_s` object. This must be performed before hashing data using SHA-1.
The SHA-1 container type (`fio_sha1_s`) is defines as follows:
The `fio_sha1_s` structure's content should be ignored.
#### `fio_sha1`
```c
inline char *fio_sha1(fio_sha1_s *s, const void *data, size_t len)
```
A SHA1 helper function that performs initialization, writing and finalizing.
SHA-1 hashing container -
The `sha1_s` type will contain all the sha1 data required to perform the
hashing, managing it's encoding. If it's stack allocated, no freeing will be
required.
#### `fio_sha1_write`
```c
void fio_sha1_write(fio_sha1_s *s, const void *data, size_t len);
```
Writes data to the sha1 buffer.
#### `fio_sha1_result`
```c
char *fio_sha1_result(fio_sha1_s *s);
```
Finalizes the SHA1 hash, returning the Hashed data.
`fio_sha1_result` can be called for the same object multiple times, but the finalization will only be performed the first time this function is called.
### SHA-2
SHA-2 example:
```c
fio_sha2_s sha2;
fio_sha2_init(&sha2, SHA_512);
fio_sha2_write(&sha2,
"The quick brown fox jumps over the lazy dog", 43);
char *hashed_result = fio_sha2_result(&sha2);
```
#### `fio_sha2_init`
```c
fio_sha2_s fio_sha2_init(fio_sha2_variant_e variant);
```
Initializes / resets the SHA-2 object.
SHA-2 is actually a family of functions with different variants. When initializing the SHA-2 container, a variant must be chosen. The following are valid variants:
* `SHA_512`
* `SHA_384`
* `SHA_512_224`
* `SHA_512_256`
* `SHA_256`
* `SHA_224`
The `fio_sha2_s` structure's content should be ignored.
#### `fio_sha2_write`
```c
void fio_sha2_write(fio_sha2_s *s, const void *data, size_t len);
```
Writes data to the SHA-2 buffer.
#### `fio_sha2_result`
```c
char *fio_sha2_result(fio_sha2_s *s);
```
Finalizes the SHA-2 hash, returning the Hashed data.
`sha2_result` can be called for the same object multiple times, but the finalization will only be performed the first time this function is called.
#### `fio_sha2_512`
```c
inline char *fio_sha2_512(fio_sha2_s *s, const void *data,
size_t len);
```
A SHA-2 helper function that performs initialization, writing and finalizing.
Uses the SHA2 512 variant.
#### `fio_sha2_256`
```c
inline char *fio_sha2_256(fio_sha2_s *s, const void *data,
size_t len);
```
A SHA-2 helper function that performs initialization, writing and finalizing.
Uses the SHA2 256 variant.
#### `fio_sha2_384`
```c
inline char *fio_sha2_384(fio_sha2_s *s, const void *data,
size_t len);
```
A SHA-2 helper function that performs initialization, writing and finalizing.
Uses the SHA2 384 variant.
## Version and Compilation Related Macros
The following macros effect facil.io's compilation and can be used to validate the API version exposed by the library.
### Version Macros
The version macros relate the version for both facil.io's core library and it's bundled extensions.
#### `FIO_VERSION_MAJOR`
The major version macro is currently zero (0), since the facil.io library's API should still be considered unstable.
In the future, API breaking changes will cause this number to change.
#### `FIO_VERSION_MINOR`
The minor version normally represents new feature or substantial changes that don't effect existing API.
However, as long as facil.io's major version is zero (0), API breaking changes will cause the minor version (rather than the major version) to change.
#### `FIO_VERSION_PATCH`
The patch version is usually indicative to bug fixes.
However, as long as facil.io's major version is zero (0), new feature or substantial changes will cause the patch version to change.
#### `FIO_VERSION_BETA`
A number representing a pre-release beta version.
This indicates the API might change without notice and effects the `FIO_VERSION_STRING`.
#### `FIO_VERSION_STRING`
This macro translates to facil.io's literal string. It can be used, for example, like this:
```c
printf("Running facil.io version" FIO_VERSION_STRING "\r\n");
```
### Logging Macros
facil.io provides a number of possible logging levels and macros for easy logging to `stderr`.
#### `FIO_LOG_LEVEL`
```c
extern size_t FIO_LOG_LEVEL;
```
This variable sets / gets the logging level. Supported values include:
```c
/** Logging level of zero (no logging). */
#define FIO_LOG_LEVEL_NONE 0
/** Log fatal errors. */
#define FIO_LOG_LEVEL_FATAL 1
/** Log errors and fatal errors. */
#define FIO_LOG_LEVEL_ERROR 2
/** Log warnings, errors and fatal errors. */
#define FIO_LOG_LEVEL_WARNING 3
/** Log every message (info, warnings, errors and fatal errors). */
#define FIO_LOG_LEVEL_INFO 4
/** Log everything, including debug messages. */
#define FIO_LOG_LEVEL_DEBUG 5
```
#### `FIO_LOG_FATAL`
```c
#define FIO_LOG_FATAL(...)
```
Logs a fatal error. Doesn't exit the program (this should be performed after the logging).
Logging macros accept `printf` type arguments. i.e.:
```c
FIO_LOG_FATAL("The meaning of life: %d", 42);
```
#### `FIO_LOG_ERROR`
```c
#define FIO_LOG_ERROR(...)
```
Logs an error.
Logging macros accept `printf` type arguments. i.e.:
```c
FIO_LOG_ERROR("The meaning of life: %d", 42);
```
#### `FIO_LOG_WARNING`
```c
#define FIO_LOG_WARNING(...)
```
Logs a warning message.
Logging macros accept `printf` type arguments. i.e.:
```c
FIO_LOG_WARNING("The meaning of life: %d", 42);
```
#### `FIO_LOG_INFO`
```c
#define FIO_LOG_INFO(...)
```
Logs an information level message.
Logging macros accept `printf` type arguments. i.e.:
```c
FIO_LOG_INFO("The meaning of life: %d", 42);
```
#### `FIO_LOG_DEBUG`
```c
#define FIO_LOG_DEBUG(...)
```
Logs a debug message.
Logging macros accept `printf` type arguments. i.e.:
```c
FIO_LOG_DEBUG("The meaning of life: %d", 42);
```
#### `FIO_LOG_LENGTH_LIMIT`
```c
#define FIO_LOG_LENGTH_LIMIT 2048
```
(since 0.7.0.beta3)
Since logging uses stack memory rather than dynamic allocation, it's memory usage must be limited to avoid exploding the stack. The following sets the maximum stack memory used when logging events.
Log messages that exceed this length will result in the message *ERROR: log line output too long (can't write)*.
### Compilation Macros
The facil.io core library has some hard coded values that can be adjusted by defining the following macros during compile time.
#### `FIO_MAX_SOCK_CAPACITY`
This macro define the maximum hard coded number of connections per worker process.
To be more accurate, this number represents the highest `fd` value allowed by library functions.
If the soft coded OS limit is higher than this number, than this limit will be enforced instead.
#### `FIO_ENGINE_POLL`, `FIO_ENGINE_EPOLL`, `FIO_ENGINE_KQUEUE`
If set, facil.io will prefer the specified polling system call (`poll`, `epoll` or `kqueue`) rather then attempting to auto-detect the correct system call.
To set any of these flag while using the facil.io `makefile`, set the `FIO_FORCE_POLL` / `FIO_FORCE_EPOLL` / `FIO_FORCE_KQUEUE` environment variable to true. i.e.:
```bash
FIO_FORCE_POLL=1 make
```
It should be noted that for most use-cases, `epoll` and `kqueue` will perform better.
#### `FIO_CPU_CORES_LIMIT`
The facil.io startup procedure allows for auto-CPU core detection.
Sometimes it would make sense to limit this auto-detection to a lower number, such as on systems with more than 32 cores.
This is only relevant to automated values, when running facil.io with zero threads and processes, which invokes a large matrix of workers and threads (see [facil_start](#fio_start)).
This does NOT effect manually set (non-zero) worker/thread values.
#### `FIO_DEFER_THROTTLE_PROGRESSIVE`
The progressive throttling model makes concurrency and parallelism more likely.
Otherwise threads are assumed to be intended for "fallback" in case of slow user code, where a single thread should be active most of the time and other threads are activated only when that single thread is slow to perform.
By default, `FIO_DEFER_THROTTLE_PROGRESSIVE` is true (1).
#### `FIO_POLL_MAX_EVENTS`
This macro sets the maximum number of IO events facil.io will pre-schedule at the beginning of each cycle, when using `epoll` or `kqueue` (not when using `poll`).
Since this requires stack pre-allocated memory, this number shouldn't be set too high. Reasonable values range from 8 to 160.
The default value is currently 64.
#### `FIO_USE_URGENT_QUEUE`
This macro can be used to disable the priority queue given to outbound IO.
#### `FIO_PUBSUB_SUPPORT`
If true (1), compiles the facil.io pub/sub API. By default, this is true.
## Weak functions
Weak functions are functions that can be overridden during the compilation / linking stage.
This provides control over some operations such as thread creation and process forking, which could be important when integrating facil.io into a VM engine such as Ruby or JavaScript.
### Forking
#### `fio_fork`
```c
int fio_fork(void);
```
OVERRIDE THIS to replace the default `fork` implementation.
Should behaves like the system's `fork`.
Current implementation simply calls [`fork`](http://man7.org/linux/man-pages/man2/fork.2.html).
### Thread Creation
#### `fio_thread_new`
```c
void *fio_thread_new(void *(*thread_func)(void *), void *arg);
```
OVERRIDE THIS to replace the default `pthread` implementation.
Accepts a pointer to a function and a single argument that should be executed
within a new thread.
The function should allocate memory for the thread object and return a
pointer to the allocated memory that identifies the thread.
On error NULL should be returned.
The default implementation returns a `pthread_t *`.
#### `fio_thread_free`
```c
void fio_thread_free(void *p_thr);
```
OVERRIDE THIS to replace the default `pthread` implementation.
Frees the memory associated with a thread identifier (allows the thread to
run it's course, just the identifier is freed).
#### `fio_thread_join`
```c
int fio_thread_join(void *p_thr);
```
OVERRIDE THIS to replace the default `pthread` implementation.
Accepts a pointer returned from `fio_thread_new` (should also free any
allocated memory) and joins the associated thread.
Return value is ignored.
# Risky Hash
Risky Hash is a keyed hashing function which was inspired by both xxHash and SipHash.
It's meant to provide a fast alternative to SipHash.
Risky Hash wasn't properly tested for attack resistance and shouldn't be used with any data that might be malicious, since it's unknown if this could result in [hash flooding attacks](http://emboss.github.io/blog/2012/12/14/breaking-murmur-hash-flooding-dos-reloaded/) (see [here](https://medium.freecodecamp.org/hash-table-attack-8e4371fc5261)).
Risky Hash was tested with [`SMHasher`](https://github.com/rurban/smhasher) ([see results](#smhasher-results)) (passed).
A non-streaming [reference implementation in C is attached](#in-code). The code is easy to read and should be considered an integral part of this specification.
## Status
> Risky Hash is still under development and review. This specification should be considered a working draft.
> Risky Hash should be limited to testing and safe environments until it's fully analyzed and reviewed.
This is the second draft of the RiskyHash algorithm and it incorporates updates from community driven feedback.
* Florian Weber (@Florianjw) [exposed coding errors (last 8 byte ordering) and took a bit of time to challenge the algorithm](https://www.reddit.com/r/crypto/comments/9kk5gl/break_my_ciphercollectionpost/eekxw2f/?context=3) and make a few suggestions.
Following this feedback, the error in the code was fixed, the initialization state was updated and the left-over bytes are now read in order with padding (rather than consumed by the 4th state-word).
* Chris Anderson (@injinj) did amazing work exploring a 128 bit variation and attacking RiskyHash using a variation on a Meet-In-The-Middle attack, written by Henning Makholm (@hmakholm), that discovers hash collisions with a small Hamming distance ([SMHasher fork](https://github.com/hmakholm/smhasher)).
Following this input, RiskyHash updated the way the message length is incorporated into the final hash and updated the consumption stage to replace the initial XOR with ADD.
The [previous version can be accessed here](https://facil.io/0.7.x/riskyhash_v1/).
## Purpose
Risky Hash is designed for fast Hash Map key calculation for both big and small keys. It attempts to act as a 64 bit keyed PRF.
It's possible to compile facil.io with Risk Hash as the default hashing function (the current default is SipHash1-3) by defining the `FIO_USE_RISKY_HASH` during compilation (`-DFIO_USE_RISKY_HASH`).
## Algorithm
A non-streaming C implementation can be found at the `fio.h` header, in the static function: `fio_risky_hash` [and later on in this document](#in-code).
Risky Hash uses 4 reading vectors (state-words), each containing 64 bits.
Risky Hash requires a 64 bit "secret". Zero is a valid secret.
Risky Hash uses the following prime numbers:
```txt
P[0] = 0xFBBA3FA15B22113B
P[1] = 0xAB137439982B86C9
```
Risky Hash has three stages:
* Initialization stage.
* Reading stage.
* Mixing stage.
The hashing algorithm uses an internal 256 bit state for a 64 bit output and the input data is mixed twice into the state on different bit positions (left rotation).
This approach **should** minimize the risk of malicious data weakening the hash function.
The following operations are used:
* `~` marks a bit inversion.
* `+` marks a mod 2^64 addition.
* `XOR` marks an XOR operation.
* `MUL(x,y)` a mod 2^64 multiplication.
* `LROT(x,bits)` is a left rotation of a 64 bit word.
* `>>` is a right shift (not rotate, some bits are lost).
* `<<` is a left shift (not rotate, some bits are lost).
### Initialization
In the initialization stage, Risky Hash attempts to achieves three goals:
* Initialize the hash state using a "secret" (key / salt / seed) in a way that will result in the "secret" having a meaningful impact on the final result.
* Initialize the state in a way that can't be reversed/controlled by a maliciously crafted message.
* Initialize the state with minimal bias (bits have a fairly even chance at being set or unset).
The four consumption vectors are initialized using the seed ("secret") like so:
```txt
V1 = seed XOR P[1]
V2 = (~seed) + P[1]
V3 = LROT(seed, 17) XOR ((~P[0]) + P[1])
V4 = LROT(seed, 33) + (~P[1])
```
### Consumption
In the consumption stage, Risky Hash attempts to achieves three goals:
* Allow parallelism.
This is achieved by using a number of distinct and separated reading "vectors" (independent state-words).
* Repeated data blocks should produce different results according to their position.
This is achieved by performing left-rotation and prime multiplication during each round, so the vector is mutated every step (regardless of the input data).
* Maliciously crafted data won't be able to weaken the hash function or expose the "secret".
This is achieved by reading the data twice into different positions in the consumption vector. This minimizes the possibility of finding a malicious value that could break the state vector.
Risky Hash consumes data in 64 bit chunks/words.
Each vector reads a single 64 bit word within a 256 bit block, allowing the vectors to be parallelized though any message length of 256 bits or longer.
`V1` reads the first 64 bits, `V2` reads bits 65-128, and so forth...
The 64 bits are read in network byte order (Big-Endian) and treated as a numerical value.
Any trailing data that doesn't fit in a 64 bit word is padded with zeros and consumed **in order** (by the next available vector).
Each vector performs the following operations in each of it's consumption rounds (`V` is the vector, `word` is the input data for that vector as a 64 bit word):
```txt
V = V + word
V = LROT(V, 33)
V = V + word
V = MUL(V, P[0])
```
It is normal for some vectors to perform more consumption rounds than others when the data isn't divisible into 256 bit blocks.
### Hash Mixing
In the mixing stage, Risky Hash attempts to achieves three goals:
* Be irreversible.
This stage is the "last line of defense" against malicious data or possible collisions. For this reason, it should be infeasible to extract meaningful data from the final result.
* Produce a message digest with minimal bias (bits have a fairly even chance at being set or unset).
* Allow all consumption vectors an equal but different effect on the final hash value.
The following intermediate 64 bit result is calculated:
```txt
result = LROT(V1,17) + LROT(V2,13) + LROT(V3,47) + LROT(V4,57)
```
At this point the length of the input data is finalized an can be added to the calculation.
The consumed (unpadded) message length is treated as a 64 bit word. It is shifted left by 33 bits and XORed with itself. Then, the updated length value is added to the intermediate result:
```txt
length = length XOR (length << 33);
result = result + length
```
The vectors are mixed in with the intermediate result using prime number multiplication to minimize any bias:
```txt
result = result + MUL(V1, P[1])
result = result XOR LROT(result, 13);
result = result + MUL(V2, P[1])
result = result XOR LROT(result, 29);
result = result + MUL(V3, P[1])
result = result XOR LROT(result, 33);
result = result + MUL(V4, P[1])
result = result XOR LROT(result, 51);
```
Finally, the intermediate result is mixed with itself to improve bit entropy distribution and hinder reversibility.
```txt
result = result XOR MUL(P[0], (result >> 29) )
```
## Performance
Risky Hash attempts to balance performance with security concerns, since hash functions are often use by insecure hash table implementations.
However, the design should allow for fairly high performance, for example, by using SIMD instructions or a multi-threaded approach (up to 4 threads).
In fact, even the simple reference implementation at the end of this document offers fairly high performance.
## Attacks, Reports and Security
No known attacks exist for this draft of the Risky Hash algorithm and no known collisions have been found...
...However, it's too early to tell.
At this early stage, please feel free to attack the Risky Hash algorithm and report any security concerns in the [GitHub issue tracker](https://github.com/boazsegev/facil.io/issues).
Later, as Risky Hash usage might increase, attacks should be reported discretely if possible, allowing for a fix to be provided before publication.
## In Code
In C code, the above description might translate like so:
```c
/*
Copyright: Boaz Segev, 2019
License: MIT
*/
/** 64 bit left rotation, inlined. */
#define fio_lrot64(i, bits) \
(((uint64_t)(i) << ((bits)&63UL)) | ((uint64_t)(i) >> ((-(bits)) & 63UL)))
/** Converts an unaligned network ordered byte stream to a 64 bit number. */
#define fio_str2u64(c) \
((uint64_t)((((uint64_t)((uint8_t *)(c))[0]) << 56) | \
(((uint64_t)((uint8_t *)(c))[1]) << 48) | \
(((uint64_t)((uint8_t *)(c))[2]) << 40) | \
(((uint64_t)((uint8_t *)(c))[3]) << 32) | \
(((uint64_t)((uint8_t *)(c))[4]) << 24) | \
(((uint64_t)((uint8_t *)(c))[5]) << 16) | \
(((uint64_t)((uint8_t *)(c))[6]) << 8) | \
((uint64_t)0 + ((uint8_t *)(c))[7])))
#ifdef __cplusplus
/* the register keyword was deprecated for C++ but is semi-meaningful in C */
#define register
#endif
/* Risky Hash primes */
#define RISKY_PRIME_0 0xFBBA3FA15B22113B
#define RISKY_PRIME_1 0xAB137439982B86C9
/* Risky Hash consumption round, accepts a state word s and an input word w */
#define fio_risky_consume(v, w) \
(v) += (w); \
(v) = fio_lrot64((v), 33); \
(v) += (w); \
(v) *= RISKY_PRIME_0;
/* Computes a facil.io Risky Hash. */
uint64_t fio_risky_hash(const void *data_, size_t len,
uint64_t seed) {
/* reading position */
const uint8_t *data = (uint8_t *)data_;
/* The consumption vectors initialized state */
register uint64_t v0 = seed ^ RISKY_PRIME_1;
register uint64_t v1 = ~seed + RISKY_PRIME_1;
register uint64_t v2 =
fio_lrot64(seed, 17) ^ ((~RISKY_PRIME_1) + RISKY_PRIME_0);
register uint64_t v3 = fio_lrot64(seed, 33) + (~RISKY_PRIME_1);
/* consume 256 bit blocks */
for (size_t i = len >> 5; i; --i) {
fio_risky_consume(v0, fio_str2u64(data));
fio_risky_consume(v1, fio_str2u64(data + 8));
fio_risky_consume(v2, fio_str2u64(data + 16));
fio_risky_consume(v3, fio_str2u64(data + 24));
data += 32;
}
/* Consume any remaining 64 bit words. */
switch (len & 24) {
case 24:
fio_risky_consume(v2, fio_str2u64(data + 16));
case 16: /* overflow */
fio_risky_consume(v1, fio_str2u64(data + 8));
case 8: /* overflow */
fio_risky_consume(v0, fio_str2u64(data));
data += len & 24;
}
uint64_t tmp = 0;
/* consume leftover bytes, if any */
switch ((len & 7)) {
case 7: /* overflow */
tmp |= ((uint64_t)data[6]) << 8;
case 6: /* overflow */
tmp |= ((uint64_t)data[5]) << 16;
case 5: /* overflow */
tmp |= ((uint64_t)data[4]) << 24;
case 4: /* overflow */
tmp |= ((uint64_t)data[3]) << 32;
case 3: /* overflow */
tmp |= ((uint64_t)data[2]) << 40;
case 2: /* overflow */
tmp |= ((uint64_t)data[1]) << 48;
case 1: /* overflow */
tmp |= ((uint64_t)data[0]) << 56;
/* ((len >> 3) & 3) is a 0...3 value indicating consumption vector */
switch ((len >> 3) & 3) {
case 3:
fio_risky_consume(v3, tmp);
break;
case 2:
fio_risky_consume(v2, tmp);
break;
case 1:
fio_risky_consume(v1, tmp);
break;
case 0:
fio_risky_consume(v0, tmp);
break;
}
}
/* merge and mix */
uint64_t result = fio_lrot64(v0, 17) + fio_lrot64(v1, 13) +
fio_lrot64(v2, 47) + fio_lrot64(v3, 57);
len ^= (len << 33);
result += len;
result += v0 * RISKY_PRIME_1;
result ^= fio_lrot64(result, 13);
result += v1 * RISKY_PRIME_1;
result ^= fio_lrot64(result, 29);
result += v2 * RISKY_PRIME_1;
result ^= fio_lrot64(result, 33);
result += v3 * RISKY_PRIME_1;
result ^= fio_lrot64(result, 51);
/* irreversible avalanche... I think */
result ^= (result >> 29) * RISKY_PRIME_0;
return result;
}
#undef fio_risky_consume
#undef FIO_RISKY_PRIME_0
#undef FIO_RISKY_PRIME_1
```
## SMHasher results
The following results were produced on a 2.9 GHz Intel Core i9 machine and won't be updated every time.
```txt
-------------------------------------------------------------------------------
--- Testing RiskyHash "facil.io hashing (by Bo)"
[[[ Sanity Tests ]]]
Verification value 0x13AA4AB6 : PASS
Running sanity check 1 ..........PASS
Running AppendedZeroesTest..........PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 5.838 bytes/cycle - 16702.48 MiB/sec @ 3 ghz
Alignment 6 - 5.838 bytes/cycle - 16702.90 MiB/sec @ 3 ghz
Alignment 5 - 5.838 bytes/cycle - 16703.48 MiB/sec @ 3 ghz
Alignment 4 - 5.830 bytes/cycle - 16680.17 MiB/sec @ 3 ghz
Alignment 3 - 5.840 bytes/cycle - 16707.69 MiB/sec @ 3 ghz
Alignment 2 - 5.840 bytes/cycle - 16708.61 MiB/sec @ 3 ghz
Alignment 1 - 5.842 bytes/cycle - 16714.10 MiB/sec @ 3 ghz
Alignment 0 - 5.828 bytes/cycle - 16673.79 MiB/sec @ 3 ghz
Average - 5.837 bytes/cycle - 16699.15 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 25.00 cycles/hash
Small key speed test - 2-byte keys - 27.00 cycles/hash
Small key speed test - 3-byte keys - 28.00 cycles/hash
Small key speed test - 4-byte keys - 28.89 cycles/hash
Small key speed test - 5-byte keys - 28.98 cycles/hash
Small key speed test - 6-byte keys - 28.49 cycles/hash
Small key speed test - 7-byte keys - 28.95 cycles/hash
Small key speed test - 8-byte keys - 32.24 cycles/hash
Small key speed test - 9-byte keys - 32.00 cycles/hash
Small key speed test - 10-byte keys - 32.21 cycles/hash
Small key speed test - 11-byte keys - 32.12 cycles/hash
Small key speed test - 12-byte keys - 32.00 cycles/hash
Small key speed test - 13-byte keys - 32.00 cycles/hash
Small key speed test - 14-byte keys - 32.00 cycles/hash
Small key speed test - 15-byte keys - 32.17 cycles/hash
Small key speed test - 16-byte keys - 32.00 cycles/hash
Small key speed test - 17-byte keys - 32.85 cycles/hash
Small key speed test - 18-byte keys - 32.41 cycles/hash
Small key speed test - 19-byte keys - 32.29 cycles/hash
Small key speed test - 20-byte keys - 32.28 cycles/hash
Small key speed test - 21-byte keys - 32.50 cycles/hash
Small key speed test - 22-byte keys - 33.21 cycles/hash
Small key speed test - 23-byte keys - 32.00 cycles/hash
Small key speed test - 24-byte keys - 32.00 cycles/hash
Small key speed test - 25-byte keys - 32.42 cycles/hash
Small key speed test - 26-byte keys - 32.90 cycles/hash
Small key speed test - 27-byte keys - 32.42 cycles/hash
Small key speed test - 28-byte keys - 33.08 cycles/hash
Small key speed test - 29-byte keys - 32.31 cycles/hash
Small key speed test - 30-byte keys - 32.58 cycles/hash
Small key speed test - 31-byte keys - 32.68 cycles/hash
Average 31.355 cycles/hash
[[[ Differential Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ Avalanche Tests ]]]
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.655333%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.728000%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.738000%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.690667%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.675333%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.698000%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.667333%
Testing 88-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.691333%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.692000%
Testing 104-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.684000%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.709333%
Testing 120-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.754000%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.714667%
Testing 136-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.790667%
Testing 144-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.672000%
Testing 152-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.702667%
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 12 - 0.044%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 6 - 0.028%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 59 - 0.034%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 1 - 0.044%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 2 - 0.029%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 16-bit window at bit 4 - 0.129%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 42 - 0.058%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.014%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 32 - 0.008%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 32 - 0.003%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 32-bit keys with up to 6 bits set - 1149017 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 17-bit window at bit 57 - 0.112%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 42 - 0.048%
Keyset 'Sparse' - 48-bit keys with up to 5 bits set - 1925357 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 18 - 0.104%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 45 - 0.045%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.041%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 54 - 0.069%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 56 - 0.131%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 34 - 0.067%
[[[ Keyset 'Combination Lowbits' Tests ]]]
Keyset 'Combination' - up to 8 blocks from a set of 8 - 19173960 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 31 - 0.012%
[[[ Keyset 'Combination Highbits' Tests ]]]
Keyset 'Combination' - up to 8 blocks from a set of 8 - 19173960 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 12 - 0.017%
[[[ Keyset 'Combination 0x8000000' Tests ]]]
Keyset 'Combination' - up to 20 blocks from a set of 2 - 2097150 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.102%
[[[ Keyset 'Combination 0x0000001' Tests ]]]
Keyset 'Combination' - up to 20 blocks from a set of 2 - 2097150 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.092%
[[[ Keyset 'Combination Hi-Lo' Tests ]]]
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 2 - 0.040%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Windowed' - 128-bit key, 20-bit window - 128 tests, 1048576 keys per test
Window at 0 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 1 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 2 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 3 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 4 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 5 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 6 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 7 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 8 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 9 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 10 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 11 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 12 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 13 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 14 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 15 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 16 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 17 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 18 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 19 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 20 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 21 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 22 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 23 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 24 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 25 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 26 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 27 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 28 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 29 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 30 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 31 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 32 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 33 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 34 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 35 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 36 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 37 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 38 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 39 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 40 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 41 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 42 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 43 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 44 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 45 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 46 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 47 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 48 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 49 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 50 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 51 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 52 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 53 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 54 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 55 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 56 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 57 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 58 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 59 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 60 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 61 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 62 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 63 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 64 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 65 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 66 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 67 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 68 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 69 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 70 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 71 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 72 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 73 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 74 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 75 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 76 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 77 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 78 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 79 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 80 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 81 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 82 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 83 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 84 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 85 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 86 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 87 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 88 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 89 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 90 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 91 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 92 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 93 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 94 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 95 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 96 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 97 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 98 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 99 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 100 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 101 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 102 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 103 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 104 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 105 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 106 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 107 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 108 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 109 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 110 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 111 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 112 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 113 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 114 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 115 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 116 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 117 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 118 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 119 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 120 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 121 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 122 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 123 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 124 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 125 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 126 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 127 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 128 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "Foo[XXXX]Bar" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 62 - 0.020%
Keyset 'Text' - keys of form "FooBar[XXXX]" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 15 - 0.024%
Keyset 'Text' - keys of form "[XXXX]FooBar" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 59 - 0.022%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 65536 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 13-bit window at bit 40 - 0.442%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 1000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 17-bit window at bit 53 - 0.162%
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 684.013358 seconds
-------------------------------------------------------------------------------
```
# core API and extensions
facil.io's [core library](https://facil.io/0.7.x/fio/) (the `fio.h` and `fio.c` files) is a stand alone two-file library, which can be easily used in different projects.
However, there are many tasks that are common to applications that need an evented networking library such as facil.io.
These bundled extensions to the core library provide common functionality. They aren't required and can be safely deleted without effecting the core library.
The following extensions come bundled along with facil.io:
* The [TLS (`fio_tls`)](https://facil.io/0.7.x/fio_tls/) extension for adding SSL/TLS support (requires 3rd party libraries).
* The [CLI (`fio_cli`)](https://facil.io/0.7.x/fio_cli/) extension for handling command line arguments.
* The [`FIOBJ`](https://facil.io/0.7.x/fiobj/) extension - this extension defines dynamic soft types and JSON helpers, making it easier to handle network bound information which often contains mixed types.
* The [`http`](https://facil.io/0.7.x/http/) extension - this compound module is everything HTTP and it requires the FIOBJ extension.
* The [`websockets`](websockets) extension - this is part of the HTTP module and extends it to support Websocket connections.
* The [`redis`](https://facil.io/0.7.x/redis/) extension adds Redis connectivity to the core pub/sub service, making horizontal scaling a breeze.
# Dynamic Type System: facil.io objects (`FIOBJ`)
In this page you will find a general overview. For detailed API information please visit the Core or Type pages.
To use the facil.io FIOBJ API, include the file `fiobj.h`
## Overview
`facil.io` offers a dynamic type system that makes it a breeze to mix object types together.
This dynamic type system is an independent module within the `facil.io` core and can be used separately.
The `FIOBJ` type API is divided by it's inner types (tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`):
* [Core and Generic API](https://facil.io/0.7.x/fiobj_core/)
* [Primitive Types](https://facil.io/0.7.x/fiobj_primitives/)
* [Number / Float](https://facil.io/0.7.x/fiobj_numbers/)
* [String](https://facil.io/0.7.x/fiobj_str/)
* [Array](https://facil.io/0.7.x/fiobj_ary/)
* [Hash](https://facil.io/0.7.x/fiobj_hash/)
* [Data](https://facil.io/0.7.x/fiobj_data/)
* [JSON](https://facil.io/0.7.x/fiobj_json/)
* [Mustache](https://facil.io/0.7.x/fiobj_mustache/)
### Why we need dynamic types?
C doesn't lend itself easily to the dynamic types that are often used in languages such as Javascript. This makes it harder to use an optimized C backend (server) when the frontend (client / browser) expects multi-type responses such as JSON objects.
To resolve this difference in expectations, `facil.io` offers the `FIOBJ` type system.
This is an opaque type that can be tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`.
This offers the following advantages (among others):
* Saves you precious development time.
* Allows deep integration with `facil.io` services, reducing the need to translate from one type to another.
* Allows for "typeless" actions, such as collection iteration (`fiobj_each2`), simple conversion (`fiobj_obj2num` and `fiobj_obj2cstr`), deallocation (`fiobj_free`). reference counting (`fiobj_dup`) and equality checks (`fiobj_iseq`).
* Offers JSON parsing and formatting to and from `FIOBJ`.
* Offers non-recursive iteration.
## API Considerations
This is a short summery regarding the API and it's use. The `fiobj_*` API is well documented in the header files, so only main guidelines are mentioned.
### Functional Access
All object access should be functional, or using the macros provided. Although this requirement can be circumvented, using the functional interface should be preferred.
For example:
```c
#include "fiobj.h"
/* this will work */
FIOBJ str = fiobj_str_buf(6); /* automatically adds room for the NUL terminator */
fio_str_info_s raw_str = fiobj_obj2cstr(str);
memcpy(raw_str.buffer, "Hello!", 6);
fiobj_str_resize(str, 6);
// ...
fiobj_free(str);
/* this is better */
FIOBJ str = fiobj_str_buf(6);
fiobj_str_write(str, "Hello!", 6);
// ...
fiobj_free(str);
/* for simple strings, one line will do */
FIOBJ str = fiobj_str_new("Hello!", 6);
// ...
fiobj_free(str);
/* for more complex cases, printf style is supported */
FIOBJ str = fiobj_str_buf(1); // note that 0 == whole memory page
fiobj_str_printf("%s %d" , "Hello!", 42)
// ...
fiobj_free(str);
```
### Ownership Follows Nesting
An object's memory should *always* be managed by it's "owner". This usually means the calling function.
*However*, when an object is nested within another object (i.e., placed in an Array or set as the *value* for a Hash or an HTTP header), **the ownership of the object is transferred**.
In the following example, the String nested within the Array is freed when the Array is freed:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ str = fiobj_str_new("Hello!", 6);
fiobj_ary_push(ary, str);
// ...
fiobj_free(ary);
```
Hashes follow the same rule. However...
It's important to note that **Hash keys ownership isn't transferred to the Hash** (keys are used to access and store data, but they are not the data itself).
When calling `fiobj_hash_set`, we are storing a *value* in the Hash, the *key* is what we use to access that value. This is why **the key's ownership remains with the calling function**. i.e.:
```c
FIOBJ h = fiobj_hash_new();
FIOBJ key = fiobj_str_new("life", 4);
/* By placing the Number in the Hash, it will be deallocated together with the Hash */
fiobj_hash_set(h, key, fiobj_num_new(42));
// ...
fiobj_free(h); /* Free the Hash and it's data, but NOT the key */
// ...
/* eventually we need to free the key */
fiobj_free(key);
```
### Passing By Reference
All objects are passed along by reference. The `dup` (duplication) process simply increases the reference count.
This is a very powerful tool. In the following example, `str2` is a "copy" **by reference** of `str`. By editing `str2` we're also editing `str`:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
FIOBJ str2 = fiobj_dup(str);
/* We'll edit str2 to say "Hello There!" instead of "Hello!" */
fiobj_str_resize(str2, 5);
fiobj_str_write(str2, " There!", 7);
/* This prints "Hello There!" because str was edited by reference! */
printf("%s\n", fiobj_obj2cstr(str).data);
/* we need to free both references to free the memory */
fiobj_free(str);
fiobj_free(str2);
```
An independent copy can be created using an object's specific copy function. This example create a new, independent, object instead of referencing the old one:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
/* create a copy instead of a reference */
FIOBJ str2 = fiobj_str_copy(str);
/* this is the same as */
FIOBJ str3 = fiobj_str_new(fiobj_obj2cstr(str).data, fiobj_obj2cstr(str).len);
// ...
fiobj_free(str);
fiobj_free(str2);
fiobj_free(str3);
```
Copy by reference produces a deep reference adjustment, so Arrays and Hashes can be safely copied by reference.
```c
FIOBJ ary = fiobj_ary_new();
fiobj_ary_push(ary, fiobj_str_new("Hello!", 6));
FIOBJ ary_copy = fiobj_dup(ary);
// ...
fiobj_free(ary);
// all the items in ary2 are still accessible.
fprintf(stderr, "%s\n", fiobj_obj2cstr( fiobj_ary_index(ary_copy, -1) ).buffer );
fiobj_free(ary_copy);
```
### Cyclic Nesting Errors
Cyclic protection is unsupported mostly because of performance concerns, but also because cyclic nesting is impractical for network applications (for example, how would a cyclic object be formatted into JSON?).
Cyclic nesting should be avoided. For example, the following code will (at best case scenario) crash:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ ary2 = fiobj_ary_new();
// cyclic nesting
fiobj_ary_push(ary, ary2);
fiobj_ary_push(ary2, ary);
// free might crash or produce unexpected results
fiobj_free(ary);
// each2 will cycle forever
fiobj_each2(ary2, ...);
```
## Independence
The `FIOBJ` module is independent and can be extracted from `facil.io` by copying the `fiobj.h` file (under `lib/facil/core/types`) and all the files in the `lib/facil/core/types/fiobj` folder.
Place these files in your project and use to your heart's content.
The module is licensed under the same MIT license offered by the rest of the `facil.io` source code.
# Risky Hash
Risky Hash is a keyed hashing function which was inspired by both xxHash and SipHash.
It provides a fast alternative to SipHash when hashing safe data and a streaming variation can be easily implemented.
Risky Hash wasn't properly tested for attack resistance and shouldn't be used with any data that might be malicious, since it's unknown if this could result in [hash flooding attacks](http://emboss.github.io/blog/2012/12/14/breaking-murmur-hash-flooding-dos-reloaded/) (see [here](https://medium.freecodecamp.org/hash-table-attack-8e4371fc5261)).
Risky Hash was tested with [`SMHasher`](https://github.com/rurban/smhasher) ([see results](#smhasher-results)) (passed).
A non-streaming [reference implementation in C is attached](#in-code). The code is easy to read and should be considered as the actual specification.
## Status
Risky Hash is still under development and review. This specification should be considered a working draft.
Risky Hash should be limited to testing and safe environments until it's fully analyzed and reviewed.
## Purpose
Risky Hash is designed for fast Hash Map key calculation for both big and small keys. It attempts to act as a 64 bit keyed PRF.
It's possible to compile facil.io with Risk Hash as the default hashing function (the current default is SipHash1-3) by defining the `FIO_USE_RISKY_HASH` during compilation (`-DFIO_USE_RISKY_HASH`).
## Overview
Risky Hash has three stages:
* Initialization stage.
* Reading stage.
* Mixing stage.
The hashing algorithm uses an internal 256 bit state for a 64 bit output and the input data is mixed twice into the state, using different operations (XOR, addition) on different bit positions (left rotation).
This approach **should** minimize the risk of malicious data weakening the hash function.
### Overview: Initialization
In the initialization stage, Risky Hash attempts to achieves three goals:
* Initialize the hash state using a "secret" (key / salt / seed) in a way that will result in the "secret" having a meaningful impact on the final result.
* Initialize the state in a way that can't be reversed/controlled by a maliciously crafted message.
* Initialize the state with minimal bias (bits have a fairly even chance at being set or unset).
### Overview: Consumption (reading)
In the consumption stage, Risky Hash attempts to achieves three goals:
* Maliciously crafted data won't be able to weaken the hash function or expose the "secret".
This is achieved by reading the data twice and using a different operation each time. This minimizes the possibility of finding a malicious value that could break both operations.
* Repeated data blocks should produce different results according to their position.
This is achieved by performing left-rotation and prime multiplication in a way that causes different positions to have different side effects.
* Allow parallelism.
This is achieved by using a number of distinct and separated reading "vectors".
This also imposes a constraint about the number of registers, or "hidden variables", each vector should use.
(for example `a += a + b` could require two registers, while `a = (a * 2) + b` requires one)
It should be noted that Risky Hash consumes data in 64 bit chunks/words.
Any trailing data that doesn't fit in a 64 bit word is padded with zeros and consumed by a specific consumption vector (rather than consumed in order).
### Overview: Mixing
In the mixing stage, Risky Hash attempts to achieves three goals:
* Be irreversible.
This stage is the "last line of defense" against malicious data or possible collisions. For this reason, it should be infeasible to extract meaningful data from the final result.
* Produce a message digest with minimal bias (bits have a fairly even chance at being set or unset).
* Allow all consumption vectors an equal but different effect on the final hash value.
Until this point, Risky Hash is contained in four 64 bit hash vectors, each hashing a quarter of the input data.
At this stage, the 256 bits of data are reduced to a 64 bit result.
## Specifics
A non-streaming C implementation can be found at the `fio.h` header, in the static function: `fio_risky_hash` and later on in this document.
Risky Hash uses 4 reading vectors, each containing 64 bits.
Risky Hash requires a 64 bit "secret". Zero is a valid secret, but is highly discouraged. A rotating random number, even if exposed, is much more likely to mitigate any risks than no secret at all.
Risky Hash uses the following prime numbers:
```txt
P[0] = 0xFBBA3FA15B22113B
P[1] = 0xAB137439982B86C9
```
The following operations are used:
* `~` marks a bit inversion.
* `+` marks a mod 2^64 addition.
* `XOR` marks an XOR operation.
* `MUL(x,y)` a mod 2^64 multiplication.
* `LROT(x,bits)` is a left rotation of a 64 bit word.
* `>>` is a right shift (not rotate, some bits are lost).
### Initialization
The four consumption vectors are initialized using the seed ("secret") like so:
```txt
V1 = seed XOR P[1],
V2 = (~seed) + P[1],
V3 = LROT(seed, 17) XOR P[1],
V4 = LROT(seed, 33) + P[1],
```
### Consumption
Each vector reads a single 64 bit word within a 256 bit block, allowing the vectors to be parallelized though any message length of 256 bits or longer.
`V1` reads the first 64 bits, `V2` reads bits 65-128, and so forth...
The 64 bits are read in network byte order (Big-Endian) and treated as a numerical value.
Each vector performs the following operations in each of it's consumption rounds (`V` is the vector, `word` is the input data for that vector):
```txt
V = V XOR word
V = LROT(V, 33) + word
V = MUL(P[0], V)
```
If the data fits evenly in 64 bit words, than it will be read with no padding, even if some vectors perform more consumption rounds than others.
If the last 64 bit word is incomplete, it will be padded with zeros (0) and consumed by the last vector (`V4`), regardless of it's position within a 256 bit block.
### Hash Mixing
At this point the length of the data is finalized an can be added to the calculation.
The following intermediate 64 bit result is calculated:
```txt
result = LROT(V1,17) + LROT(V2,13) + LROT(V3,47) + LROT(V4,57)
```
The consumed (unpadded) message length is added to this word:
```txt
result = result + length
```
The vectors are mixed in with the word using prime number multiplication to minimize any bias:
```txt
result = result + MUL(V1, P[1])
result = result XOR LROT(result, 13);
result = result + MUL(V2, P[1])
result = result XOR LROT(result, 29);
result = result + MUL(V3, P[1])
result = result XOR LROT(result, 33);
result = result + MUL(V4, P[1])
result = result XOR LROT(result, 51);
```
Finally, the result is mixed with itself to improve bit entropy distribution and hinder reversibility.
```txt
result = result XOR MUL(P[0], (result >> 29) )
```
## Performance
Risky Hash attempts to balance performance with security concerns, since hash functions are often use by insecure hash table implementations.
However, the design should allow for fairly high performance, for example, by using SIMD instructions or a multi-threaded approach (up to 4 threads).
In fact, even the simple reference implementation at the end of this document offers fairly high performance, averaging 17% faster than xxHash for short keys (up to 31 bytes) and 9% slower on long keys (262.144 bytes).
## Attacks, Reports and Security
This (previous) draft of Risky Hash can be attacked using a [meet-in-the-middle / Long-Neighbor attack](https://github.com/hmakholm/smhasher/blob/master/src/LongNeighborTest.md) which was developed by Henning Makholm in January 2019.
At this early stage, please feel free to attack the Risky Hash algorithm and report any security concerns in the [GitHub issue tracker](https://github.com/boazsegev/facil.io/issues).
Later, as Risky Hash usage might increase, attacks should be reported discretely if possible, allowing for a fix to be provided before publication.
## In Code
In C code, the above description might translate like so:
```c
/*
Copyright: Boaz Segev, 2019
License: MIT
*/
/** 64Bit left rotation, inlined. */
#define fio_lrot64(i, bits) \
(((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> ((-(bits)) & 63UL)))
/** Converts an unaligned network ordered byte stream to a 64 bit number. */
#define fio_str2u64(c) \
((uint64_t)((((uint64_t)((uint8_t *)(c))[0]) << 56) | \
(((uint64_t)((uint8_t *)(c))[1]) << 48) | \
(((uint64_t)((uint8_t *)(c))[2]) << 40) | \
(((uint64_t)((uint8_t *)(c))[3]) << 32) | \
(((uint64_t)((uint8_t *)(c))[4]) << 24) | \
(((uint64_t)((uint8_t *)(c))[5]) << 16) | \
(((uint64_t)((uint8_t *)(c))[6]) << 8) | \
((uint64_t)0 + ((uint8_t *)(c))[7])))
uintptr_t risky_hash(const void *data_, size_t len, uint64_t seed) {
/* The primes used by Risky Hash */
const uint64_t primes[] = {
0xFBBA3FA15B22113B, // 1111101110111010001111111010000101011011001000100001000100111011
0xAB137439982B86C9, // 1010101100010011011101000011100110011000001010111000011011001001
};
/* The consumption vectors initialized state */
uint64_t v[4] = {
seed ^ primes[1],
~seed + primes[1],
fio_lrot64(seed, 17) ^ primes[1],
fio_lrot64(seed, 33) + primes[1],
};
/* Risky Hash consumption round */
#define fio_risky_consume(w, i) \
v[i] ^= (w); \
v[i] = fio_lrot64(v[i], 33) + (w); \
v[i] *= primes[0];
/* compilers could, hopefully, optimize this code for SIMD */
#define fio_risky_consume256(w0, w1, w2, w3) \
fio_risky_consume(w0, 0); \
fio_risky_consume(w1, 1); \
fio_risky_consume(w2, 2); \
fio_risky_consume(w3, 3);
/* reading position */
const uint8_t *data = (uint8_t *)data_;
/* consume 256bit blocks */
for (size_t i = len >> 5; i; --i) {
fio_risky_consume256(fio_str2u64(data), fio_str2u64(data + 8),
fio_str2u64(data + 16), fio_str2u64(data + 24));
data += 32;
}
/* Consume any remaining 64 bit words. */
switch (len & 24) {
case 24:
fio_risky_consume(fio_str2u64(data + 16), 2);
case 16: /* overflow */
fio_risky_consume(fio_str2u64(data + 8), 1);
case 8: /* overflow */
fio_risky_consume(fio_str2u64(data), 0);
data += len & 24;
}
uintptr_t tmp = 0;
/* consume leftover bytes, if any */
switch ((len & 7)) {
case 7: /* overflow */
tmp |= ((uint64_t)data[6]) << 56;
case 6: /* overflow */
tmp |= ((uint64_t)data[5]) << 48;
case 5: /* overflow */
tmp |= ((uint64_t)data[4]) << 40;
case 4: /* overflow */
tmp |= ((uint64_t)data[3]) << 32;
case 3: /* overflow */
tmp |= ((uint64_t)data[2]) << 24;
case 2: /* overflow */
tmp |= ((uint64_t)data[1]) << 16;
case 1: /* overflow */
tmp |= ((uint64_t)data[0]) << 8;
fio_risky_consume(tmp, 3);
}
/* merge and mix */
uint64_t result = fio_lrot64(v[0], 17) + fio_lrot64(v[1], 13) +
fio_lrot64(v[2], 47) + fio_lrot64(v[3], 57);
result += len;
result += v[0] * primes[1];
result ^= fio_lrot64(result, 13);
result += v[1] * primes[1];
result ^= fio_lrot64(result, 29);
result += v[2] * primes[1];
result ^= fio_lrot64(result, 33);
result += v[3] * primes[1];
result ^= fio_lrot64(result, 51);
/* irreversible avalanche... I think */
result ^= (result >> 29) * primes[0];
return result;
#undef fio_risky_consume256
#undef fio_risky_consume
}
```
## SMHasher results
The following results were produced on a 2.9 GHz Intel Core i9 machine and won't be updated every time.
```txt
-------------------------------------------------------------------------------
--- Testing RiskyHash "facil.io hashing (by Bo)"
[[[ Sanity Tests ]]]
Verification value 0x1A4E494A : PASS
Running sanity check 1 ..........PASS
Running AppendedZeroesTest..........PASS
[[[ Speed Tests ]]]
Bulk speed test - 262144-byte keys
Alignment 7 - 5.838 bytes/cycle - 16701.84 MiB/sec @ 3 ghz
Alignment 6 - 5.852 bytes/cycle - 16742.05 MiB/sec @ 3 ghz
Alignment 5 - 5.835 bytes/cycle - 16692.73 MiB/sec @ 3 ghz
Alignment 4 - 5.447 bytes/cycle - 15585.04 MiB/sec @ 3 ghz
Alignment 3 - 5.834 bytes/cycle - 16690.14 MiB/sec @ 3 ghz
Alignment 2 - 5.837 bytes/cycle - 16699.70 MiB/sec @ 3 ghz
Alignment 1 - 5.141 bytes/cycle - 14708.75 MiB/sec @ 3 ghz
Alignment 0 - 5.465 bytes/cycle - 15635.34 MiB/sec @ 3 ghz
Average - 5.656 bytes/cycle - 16181.95 MiB/sec @ 3 ghz
Small key speed test - 1-byte keys - 22.31 cycles/hash
Small key speed test - 2-byte keys - 23.00 cycles/hash
Small key speed test - 3-byte keys - 24.00 cycles/hash
Small key speed test - 4-byte keys - 25.00 cycles/hash
Small key speed test - 5-byte keys - 25.00 cycles/hash
Small key speed test - 6-byte keys - 25.00 cycles/hash
Small key speed test - 7-byte keys - 25.00 cycles/hash
Small key speed test - 8-byte keys - 31.00 cycles/hash
Small key speed test - 9-byte keys - 31.00 cycles/hash
Small key speed test - 10-byte keys - 31.00 cycles/hash
Small key speed test - 11-byte keys - 31.00 cycles/hash
Small key speed test - 12-byte keys - 31.00 cycles/hash
Small key speed test - 13-byte keys - 31.00 cycles/hash
Small key speed test - 14-byte keys - 31.00 cycles/hash
Small key speed test - 15-byte keys - 31.00 cycles/hash
Small key speed test - 16-byte keys - 31.00 cycles/hash
Small key speed test - 17-byte keys - 31.00 cycles/hash
Small key speed test - 18-byte keys - 31.00 cycles/hash
Small key speed test - 19-byte keys - 31.00 cycles/hash
Small key speed test - 20-byte keys - 31.00 cycles/hash
Small key speed test - 21-byte keys - 31.00 cycles/hash
Small key speed test - 22-byte keys - 31.00 cycles/hash
Small key speed test - 23-byte keys - 31.41 cycles/hash
Small key speed test - 24-byte keys - 31.00 cycles/hash
Small key speed test - 25-byte keys - 31.00 cycles/hash
Small key speed test - 26-byte keys - 31.44 cycles/hash
Small key speed test - 27-byte keys - 31.00 cycles/hash
Small key speed test - 28-byte keys - 31.48 cycles/hash
Small key speed test - 29-byte keys - 31.00 cycles/hash
Small key speed test - 30-byte keys - 31.00 cycles/hash
Small key speed test - 31-byte keys - 31.00 cycles/hash
Average 29.505 cycles/hash
[[[ Differential Tests ]]]
Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes.
1000 reps, 8303632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes.
1000 reps, 11017632000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes.
1000 reps, 2796416000 total tests, expecting 0.00 random collisions..........
0 total collisions, of which 0 single collisions were ignored
[[[ Avalanche Tests ]]]
Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.575333%
Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.660667%
Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.632667%
Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.696667%
Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.734667%
Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.766667%
Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.762667%
Testing 88-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.806000%
Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.756000%
Testing 104-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.723333%
Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.693333%
Testing 120-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.654000%
Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.799333%
Testing 136-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.824667%
Testing 144-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.715333%
Testing 152-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.664000%
[[[ Keyset 'Cyclic' Tests ]]]
Keyset 'Cyclic' - 8 cycles of 8 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 46 - 0.036%
Keyset 'Cyclic' - 8 cycles of 9 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 29 - 0.048%
Keyset 'Cyclic' - 8 cycles of 10 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 17 - 0.029%
Keyset 'Cyclic' - 8 cycles of 11 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 59 - 0.048%
Keyset 'Cyclic' - 8 cycles of 12 bytes - 10000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 14 - 0.030%
[[[ Keyset 'TwoBytes' Tests ]]]
Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 16-bit window at bit 1 - 0.151%
Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 48 - 0.059%
Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 6 - 0.020%
Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 48 - 0.008%
Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 40 - 0.005%
[[[ Keyset 'Sparse' Tests ]]]
Keyset 'Sparse' - 32-bit keys with up to 6 bits set - 1149017 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 17-bit window at bit 20 - 0.178%
Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 51 - 0.063%
Keyset 'Sparse' - 48-bit keys with up to 5 bits set - 1925357 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 27 - 0.101%
Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 21 - 0.057%
Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 62 - 0.037%
Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 19-bit window at bit 23 - 0.087%
Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 28 - 0.056%
Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.080%
[[[ Keyset 'Combination Lowbits' Tests ]]]
Keyset 'Combination' - up to 8 blocks from a set of 8 - 19173960 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 26 - 0.017%
[[[ Keyset 'Combination Highbits' Tests ]]]
Keyset 'Combination' - up to 8 blocks from a set of 8 - 19173960 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.017%
[[[ Keyset 'Combination 0x8000000' Tests ]]]
Keyset 'Combination' - up to 20 blocks from a set of 2 - 2097150 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 0 - 0.085%
[[[ Keyset 'Combination 0x0000001' Tests ]]]
Keyset 'Combination' - up to 20 blocks from a set of 2 - 2097150 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.056%
[[[ Keyset 'Combination Hi-Lo' Tests ]]]
Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 1 - 0.021%
[[[ Keyset 'Window' Tests ]]]
Keyset 'Windowed' - 128-bit key, 20-bit window - 128 tests, 1048576 keys per test
Window at 0 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 1 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 2 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 3 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 4 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 5 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 6 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 7 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 8 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 9 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 10 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 11 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 12 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 13 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 14 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 15 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 16 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 17 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 18 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 19 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 20 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 21 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 22 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 23 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 24 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 25 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 26 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 27 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 28 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 29 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 30 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 31 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 32 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 33 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 34 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 35 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 36 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 37 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 38 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 39 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 40 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 41 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 42 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 43 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 44 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 45 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 46 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 47 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 48 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 49 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 50 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 51 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 52 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 53 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 54 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 55 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 56 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 57 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 58 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 59 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 60 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 61 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 62 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 63 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 64 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 65 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 66 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 67 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 68 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 69 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 70 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 71 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 72 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 73 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 74 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 75 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 76 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 77 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 78 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 79 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 80 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 81 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 82 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 83 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 84 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 85 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 86 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 87 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 88 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 89 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 90 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 91 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 92 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 93 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 94 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 95 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 96 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 97 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 98 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 99 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 100 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 101 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 102 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 103 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 104 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 105 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 106 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 107 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 108 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 109 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 110 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 111 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 112 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 113 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 114 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 115 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 116 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 117 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 118 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 119 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 120 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 121 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 122 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 123 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 124 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 125 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 126 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 127 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Window at 128 - Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
[[[ Keyset 'Text' Tests ]]]
Keyset 'Text' - keys of form "Foo[XXXX]Bar" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 40 - 0.022%
Keyset 'Text' - keys of form "FooBar[XXXX]" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.023%
Keyset 'Text' - keys of form "[XXXX]FooBar" - 14776336 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 20-bit window at bit 44 - 0.022%
[[[ Keyset 'Zeroes' Tests ]]]
Keyset 'Zeroes' - 65536 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 13-bit window at bit 63 - 0.477%
[[[ Keyset 'Seed' Tests ]]]
Keyset 'Seed' - 1000000 keys
Testing collisions - Expected 0.00, actual 0.00 ( 0.00x)
Testing distribution - Worst bias is the 17-bit window at bit 37 - 0.064%
Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001
Verification value is 0x00000001 - Testing took 806.795982 seconds
-------------------------------------------------------------------------------
```
# a mini-framework for C web applications
A Web application in C? It's as easy as:
```c
#include "http.h" /* the HTTP facil.io extension */
// We'll use this callback in `http_listen`, to handle HTTP requests
void on_request(http_s *request);
// These will contain pre-allocated values that we will use often
FIOBJ HTTP_HEADER_X_DATA;
// Listen to HTTP requests and start facil.io
int main(int argc, char const **argv) {
// allocating values we use often
HTTP_HEADER_X_DATA = fiobj_str_new("X-Data", 6);
// listen on port 3000 and any available network binding (NULL == 0.0.0.0)
http_listen("3000", NULL, .on_request = on_request, .log = 1);
// start the server
fio_start(.threads = 1);
// deallocating the common values
fiobj_free(HTTP_HEADER_X_DATA);
// these can be used, but we're ignoring them here.
(void)argc; (void)argv;
}
// Easy HTTP handling
void on_request(http_s *request) {
http_set_cookie(request, .name = "my_cookie", .name_len = 9, .value = "data",
.value_len = 4);
http_set_header(request, HTTP_HEADER_CONTENT_TYPE,
http_mimetype_find("txt", 3));
http_set_header(request, HTTP_HEADER_X_DATA, fiobj_str_new("my data", 7));
http_send_body(request, "Hello World!\r\n", 14);
}
```
## facil.io - more than a powerful HTTP/Websockets server library.
[facil.io](http://facil.io) is an evented Network library written in C. It provides high performance TCP/IP network services by using an evented design that was tested to provide an easy solution to [the C10K problem](http://www.kegel.com/c10k.html).
[facil.io](http://facil.io) includes a mini-framework for Web Applications, with a fast HTTP / WebSocket server, integrated Pub/Sub, optional Redis connectivity, easy JSON handling, [Mustache](http://mustache.github.io) template rendering and more nifty tidbits.
[facil.io](http://facil.io) powers the [HTTP/Websockets Ruby Iodine server](https://github.com/boazsegev/iodine) and it can easily power your application as well.
[facil.io](http://facil.io) should work on Linux / BSD / macOS (and possibly CYGWIN) and is continuously tested on both Linux and macOS.
[facil.io](http://facil.io) is a source code library, making it easy to incorporate into any project. The API was designed for simplicity and extendability, which means writing new extensions and custom network protocols is easy.
[facil.io](http://facil.io)'s core library is a two-file library (`fio.h` and `fio.c`), making it easy to incorporate networking solutions into any project.
[facil.io](http://facil.io) supports both single-threaded and multi-threaded operation modes as well as a hybrid mode (multi-process with either a single-threaded or multi-threaded workers).
I used this library (including the HTTP server) on Linux, Mac OS X and FreeBSD (I had to edit the `makefile` for each environment).
## An easy chat-room example
Here's a simple WebSocket chat-room example:
```c
#include "fio_cli.h"
#include "http.h"
/* Chat-room channel name */
static fio_str_info_s CHAT_CHANNEL = {.len = 8, .data = "chatroom"};
/* *****************************************************************************
WebSocket callbacks
***************************************************************************** */
/* We'll subscribe to the channel's chat channel when a new connection opens */
static void on_open_websocket(ws_s *ws) {
/* subscription - easy as pie */
websocket_subscribe(ws, .channel = CHAT_CHANNEL, .force_text = 1);
/* notify everyone about new (named) visitors */
FIOBJ nickname = (FIOBJ)websocket_udata_get(ws);
fio_str_info_s n = fiobj_obj2cstr(nickname);
FIOBJ msg = fiobj_str_new(n.data, n.len);
fiobj_str_write(msg, " joined the chat.", 17);
pubsub_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
/* cleanup */
fiobj_free(msg);
}
/* Free the nickname, if any. */
static void on_close_websocket(intptr_t uuid, void *udata) {
FIOBJ nickname = (FIOBJ)udata;
fio_str_info_s n = fiobj_obj2cstr(nickname);
/* send notification */
FIOBJ msg = fiobj_str_new(n.data, n.len);
fiobj_str_write(msg, " left the chat.", 15);
fio_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
/* cleanup */
fiobj_free(msg);
fiobj_free(nickname);
(void)uuid;
}
/* Received a message from a client, format message for chat . */
static void handle_websocket_messages(ws_s *ws, fio_str_info_s data,
uint8_t is_text) {
FIOBJ nickname = (FIOBJ)websocket_udata_get(ws);
fio_str_info_s n = fiobj_obj2cstr(nickname);
/* allocates a dynamic string. knowing the buffer size is faster */
FIOBJ msg = fiobj_str_buf(n.len + 2 + data.len);
fiobj_str_write(msg, n.data, n.len);
fiobj_str_write(msg, ": ", 2);
fiobj_str_write(msg, data.data, data.len);
fio_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
fiobj_free(msg);
(void)(ws);
(void)(is_text);
}
/* *****************************************************************************
HTTP Handling (Upgrading to WebSocket)
***************************************************************************** */
/* Answers simple HTTP requests */
static void answer_http_request(http_s *h) {
http_set_header2(h, (fio_str_info_s){.data = "Server", .len = 6},
(fio_str_info_s){.data = "facil.example", .len = 13});
http_set_header(h, HTTP_HEADER_CONTENT_TYPE, http_mimetype_find("txt", 3));
/* this both sends the response and frees the http handler. */
http_send_body(h, "This is a simple Websocket chatroom example.", 44);
}
/* tests that the target protocol is "websockets" and upgrades the connection */
static void answer_http_upgrade(http_s *h, char *target, size_t len) {
/* test for target protocol name */
if (len != 9 || memcmp(target, "websocket", 9)) {
http_send_error(h, 400);
return;
}
FIOBJ n;
fio_str_info_s path = fiobj_obj2cstr(h->path);
if (path.len > 1) {
n = fiobj_str_new(path.data + 1, path.len - 1);
} else {
n = fiobj_str_new("Guest", 5);
}
// Websocket upgrade will use our existing response.
if (http_upgrade2ws(
h, .on_open = on_open_websocket, .on_close = on_close_websocket,
.on_message = handle_websocket_messages, .udata = (void *)n))
fiobj_free(n);
}
/* *****************************************************************************
The main function
***************************************************************************** */
int main(int argc, char const *argv[]) {
fio_cli_start(
argc, argv, 0, 0, "WebSocket chat room example using facil.io",
FIO_CLI_INT("-t number of threads"),
FIO_CLI_INT("-w number of workers"),
FIO_CLI_INT("-p port number to listen on (0 == unix socket)"),
FIO_CLI_STRING("-b address binding"),
FIO_CLI_STRING("-www a public folder from which to serve files"),
FIO_CLI_BOOL("-v logs requests to STDERR"));
fio_cli_set_default("-p", "3000");
if (http_listen(fio_cli_get("-p"), fio_cli_get("-b"),
.on_request = answer_http_request,
.on_upgrade = answer_http_upgrade,
.log = fio_cli_get_bool("-v"),
.public_folder = fio_cli_get("-www")) == -1) {
perror("Couldn't listen for HTTP / WebSocket connections");
exit(1);
}
fio_start(.threads = fio_cli_get_i("-t"), .workers = fio_cli_get_i("-w"));
fio_cli_end();
}
```
## Further reading
The code in this project is heavily commented and the header files could (and probably should) be used as the actual documentation.
However, experience shows that a quick reference guide is immensely helpful and that Doxygen documentation is ... well ... less helpful and harder to navigate (I'll leave it at that for now).
I hope you find the documentation on this website helpful.
# facil.io 0.6.x — complete documentation
# `defer` - An event scheduling library in C.
`defer` is a simple thread pool library that defaults to POSIX threads (and could be easily ported to any thread system).
It uses a combination of micro-sleep and spin-locks for load-balancing across threads, making it more performant then the conditional variable approach as well as more portable.
Unlike most thread pool libraries, `defer` allows for two pointers to be passed to each task, allowing greater versatility and optimizations. For example, `facil.io` uses this approach to perform interactions between objects without requiring any additional allocations (which is very important for larger object collections).
The library is conveniently documented inside it's `defer.h` file.
`defer` can run in a single thread mode, without using a thread pool:
```c
#include "defer.h"
#include
// an example task
void say_hi(void * arg1, void * arg2) {
(void)arg2;
printf("%s\n", (char * )arg1);
}
// an example usage
int main(void) {
defer(say_hi, "Hello There!", NULL);
printf("Running all scheduled tasks...\n");
defer_perform();
printf("Done.\n");
}
```
Or using a thread pool:
```c
#include "defer.h"
#include
#include
// an example task
void say_hi(void * arg1, void * arg2) {
printf("%s (%lu)\n", (char * )arg1, (uintptr_t)arg2);
}
// an example usage
int main(void) {
for (uintptr_t i = 0; i < 64; i++) {
defer(say_hi, "Hello There!", (void *)i);
}
printf("Starting thread pool...\n");
pool_pt pool = defer_pool_start(8);
printf("Signaling thread pool to finish...\n");
defer_pool_stop(pool);
printf("... Waiting for thread pool to finish.\n");
defer_pool_wait(pool);
printf("Done.\n");
}
```
This is actually most of the API right there, but if you need more details you can find them in the [`defer.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/defer.h).
# The Evented IO Library - KQueue/EPoll abstraction
The `evio.h` library, is a KQueue/EPoll abstraction for edge triggered events and is part of [`facil.io`'s](https://facil.io/0.6.x/facil/) core.
It should be noted that exactly like `epoll` and `kqueue`, `evio.h` might produce unexpected results if forked after initialized, since this will cause the `epoll`/`kqueue` data to be shared across processes, even though these processes will not have access to new file descriptors (i.e. `fd` 90 on one process might reference file "A" while on a different process the same `fd` (90) might reference file "B").
This documentation isn't relevant for `facil.io` users. `facil.io` implements `evio.h` callbacks and `evio.h` cannot be used without removing `facil.h` and `facil.c` from the project.
This file is here as quick reference to anyone interested in maintaining `facil.io` or learning about how it's insides work.
## Event Callbacks
Event callbacks are defined during the linking stage and are hardwired once compilation is complete.
`void evio_on_data(void *)` - called when the file descriptor has incoming data. This is **one-shot** triggered, meaning it will **not** be called again unless the `evio_add` (or `evio_set_timer`) are called.
`void evio_on_ready(void *)` - called when the file descriptor is ready to send data (outgoing).
`void evio_on_error(void *)` - called when a file descriptor raises an error while being polled.
`void evio_on_close(void *)` - called when a file descriptor was closed REMOTELY. `evio_on_close` will NOT get called when a connection is closed locally, unless using `sock.h`'s callback, the `sock_on_close` function.
**Notice**: Both EPoll and KQueue will **not** raise an event for an `fd` that was closed using the native `close` function, so unless using `sock.h` or calling `evio_on_close`, the `evio_on_close` callback will only be called for remote events.
**Notice**: The `on_open` event is missing by design, as it is expected that any initialization required for the `on_open` event will be performed before attaching the file descriptor (socket/timer/pipe) to `evio` using [`evio_add`]().
## The `evio` API
### `evio_create`
```c
intptr_t evio_create(void)
```
Creates the `epoll` or `kqueue` object.
It's impossible to add or remove file descriptors from the polling system before
calling this method.
Returns -1 on error, otherwise returns a unique value representing the `epoll`
or `kqueue` object. The returned value can be safely ignored.
**NOTE**: Once an `epoll` / `kqueue` object was opened, `fork` should be avoided,
since ALL the events will be shared among the forked processes (while not ALL
the file descriptors are expected to be shared).
### `evio_review`
```c
int evio_review(const int timeout_millisec)
```
Reviews any pending events (up to `EVIO_MAX_EVENTS`) and calls any callbacks.
Waits up to `timeout_millisec` for events to occur.
Returns -1 on error, otherwise returns the number of events handled.
### `evio_close`
```c
void evio_close(void);
```
Closes the `epoll` / `kqueue` object, releasing it's resources (important if
forking!).
### `evio_isactive`
```c
int evio_isactive(void)
```
Returns true if the evio is available for adding or removing file descriptors.
### `evio_add`
```c
int evio_add(int fd, void *callback_arg)
```
Adds a file descriptor to the polling object (ONE SHOT).
Returns -1 on error, otherwise return value is system dependent and can be
safely ignored.
### `evio_open_timer`
```c
intptr_t evio_open_timer(void)
```
Creates a timer file descriptor, system dependent.
Returns -1 on error, or a valid fd on success.
NOTE: Systems have a limit on the number of timers that can be opened.
### `evio_set_timer`
```c
intptr_t evio_set_timer(int fd, void *callback_arg, unsigned long milliseconds)
```
Adds a timer file descriptor, so that callbacks will be called for it's events.
Returns -1 on error, otherwise return value is system dependent.
## Known Issues
* On Linux, using `epoll`, the `EPOLLONESHOT` flag doesn't remove the `fd` from `epoll` descriptor, requiring `EPOLL_CTL_MOD` instead of `EPOLL_CTL_ADD`.
To support a stateless API, where the user doesn't need to remember if a specific `fd` was previously passed to the `evio_add` function, the `epoll` implementation performs two system calls for new connections instead of a single system call (`EPOLL_CTL_ADD` is performed only if `EPOLL_CTL_MOD` fails).
# A Simple Socket Library for non-blocking Sockets
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `sock.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/sock.h).
## Overview
The `sock` library was born to solve [many concerns](https://facil.io/0.6.x/sock_why/) that pop up when using the system sockets API directly.
On systems that have unreasonably high open file limits, the sock library will artificially limit the open socket count to a reasonably high default that can be adjusted during compile time using the `LIB_SOCK_MAX_CAPACITY` macro (currently, 131,072 open files).
It supports TCP/IP sockets as well as unix doamin sockets. Pipes can also be attached to the library.
The `sock.h` API can be divided into a few different categories:
- Limits
- General helper functions
- Accepting connections and opening new sockets.
- Sending and receiving data.
- Read/Write Hooks.
### example
## Constants
## Types
## General Functions
## Socket Initialization and State
## Sending / Receiving Data
## Read/Write Hooks
## Important Notes
# Fast Concurrent Memory Allocator
The system's memory allocator is usually highly optimized and very performant. In fact, for most cases, it should probably be preferred.
However, the system's memory allocator is optimized for generic unknown use-cases which means that sometimes a custom memory allocator will perform better for a subset of use-cases.
facil.io includes a memory allocator that was optimized for small (less than a page) concurrent group allocations and deallocations (calling `malloc` a bunch of times and than calling `free` a bunch of times from many different threads) with minimal or no reallocations (minimal use of `realloc`).
The `fio_mem.h` allocator will minimize overhead and lock contention, allowing for better concurrency.
## Overview
The `fio_mem.h` allocator is used the same way the system allocator is used, prefixing it's functions with `fio_` - so the `fio_malloc`, `fio_free`, `fio_realloc` and `fio_calloc` take the same parameters as `malloc`, `free`, `realloc` and `calloc` respectively.
Allocated memory is always zeroed out and aligned on a 16 byte boundary. When possible, consecutive calls to `fio_malloc` will return with a close physical memory address to the previous allocation, resulting in improved locality.
The memory allocator assumes multiple concurrent allocation/deallocation, short life spans (memory is freed shortly after it was allocated) and small allocations (`fio_realloc` almost always copies data).
These assumptions allow the allocator to ignore fragmentation within a memory "block", waiting for the whole "block" to be freed before it's memory is recycled.
This allocator should NOT be used for objects with a long life-span, because even a single persistent object will prevent the re-use of the whole memory block (128Kb by default) from which it was allocated.
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `fio_mem.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fio_mem.h).
### example
## Constants
## Types
## Functions
## Important Notes
# HTTP API
The facil.io framework offers an HTTP/1.1 and Websocket implementation which can be found in the "http" folder.
This implementation includes a naive HTTP client as well as other features.
## Overview
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `http.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/http/http.h).
### example
## Constants
## Types
## Functions
## Important Notes
# Websockets API
The facil.io framework offers an HTTP/1.1 and Websocket implementation which can be found in the "http" folder.
This implementation includes pub/sub services for Websocket connections as well as other features.
## Overview
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `websockets.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/http/websockets.h).
### example
## Constants
## Types
## Functions
## Important Notes
# Native Pub/Sub
The facil.io framework offers a native Pub/Sub implementation which can be found in the "services" folder.
This Pub/Sub implementation covers the whole process cluster and it can be easily scaled by using Redis (which isn't required except for horizontal scaling).
## Connection based vs. non-connection based Pub/Sub
The Websockets extension allows for connection based subscription in adition to the regular Pub/Sub subscriptions.
When using connection based subscription, the connection's `on_close` callback will automatically unsubscribes from any existing subscriptions.
This allows messages to be be either forwarded directly to the client or handled by a server-side callback.
An example for this approach can be seen in the [chatroom example](https://facil.io/0.6.x/#an-easy-chatroom-example) using the `websocket_subscribe` function. The same function could have been used with a server-side callback.
A non-connection based Pub/Sub allows a local event to fire whenever a message arrives.
This event allows the server to react to certain channels, allowing remotely published events (by clients or through Redis) to invoke server routines.
This approach uses the `pubsub_subscribe` function directly rather than a connection related function.
## Connecting Redis as a Pub/Sub Service
A built-in Redis engine is bundled with facil.io (it can be removed safely by deleting the folder).
It's simple to add Redis for multi-machine clustering by creating a Redis engine and setting it as the default engine.
i.e.:
```c
int main(void) {
PUBSUB_DEFAULT_ENGINE =
redis_engine_create(/** Redis server's address */
.address = "localhost", .ping_interval = 2, );
/* code */
facil_run(/* settings */);
redis_engine_destroy(PUBSUB_DEFAULT_ENGINE);
return 0;
}
```
## Connecting a custom Pub/Sub Service
It's possible. The documentation is available within the header file `pubsub.h` (using `pubsub_engine_s`).
Notice, that the `on_subscribe` will be called for all the channels in the process cluster, so it's possible to connect to the external pub/sub service from a single process (minimizing network traffic) and to publish to the whole cluster.
Also, be aware that messages arrive **at least** once. When using pattern matching, messages might arrive far more than once (i.e., if the service sends two copies, one for the channel and one for the pattern, the cluster might receive four copies, due to internal pattern matching).
Also, see known issues for lost messages when connection is disrupted (this might be pub/sub service specific).
## Details and Limitations:
* facil.io v.0.6.x uses a Hash table for the Pub/Sub channels (replacing the [4 bit trie](https://en.wikipedia.org/wiki/Trie) approach from facil.io v.0.5.x).
This means that hash collision (using SipHash) might cause excessive `memcmp` calls.
Pattern publishing (which isn't supported by Redis and shouldn't be confused with pattern subscriptions) was deprecated due to the change in data structure.
* The Redis client engine does *not* support multiple databases. This is both becasue [database scoping is ignored by Redis during pub/sub](https://redis.io/topics/pubsub#database-amp-scoping) and because [Redis Cluster doesn't support multiple databases](https://redis.io/topics/cluster-spec). This indicated that multiple database support just isn't worth the extra effort.
It's possible to manually send commands to the Redis client and overcome this issue, but disconnections will require resetting the database.
* The Redis client engine will use a single Redis connection **per process** (for publishing) + a single connection **per cluster** (for subscriptions).
Note that **outgoing** messages are guarantied to be published **at least** once (automatic network error recovery)... however, **incoming** messages might be lost due to network connectivity issues (nothing I can do about this, it's on the Redis server side).
# Command Line Helper
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `fio_cli.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/services/fio_cli.h).
## Overview
The `fio_cli.h` helper was created to make command line interfaces easier to manage, adding automatic argument documentation, aliases, string to integer conversion, etc'.
### example
## Constants
## Types
## General Functions
## Socket Initialization and State
## Sending / Receiving Data
## Read/Write Hooks
## Important Notes
# FIOBJ Primitives
The following Primitive types are defined:
#### `fiobj_null`
```c
FIOBJ fiobj_null(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
#### `fiobj_true`
```c
FIOBJ fiobj_true(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
#### `fiobj_false`
```c
FIOBJ fiobj_false(void);
```
A `null` primitive object.
Although primitive objects aren't dynamically allocated, it's good practice to call `fiobj_free` to "deallocate" them all the same.
# FIOBJ Numbers API
### Numbers API (Integers)
#### `fiobj_num_new`
```c
FIOBJ fiobj_num_new(intptr_t num);
```
Creates a Number object. Remember to use `fiobj_free`.
#### `fiobj_num_tmp`
```c
FIOBJ fiobj_num_tmp(intptr_t num);
```
Creates a temporary Number object. Avoid using `fiobj_free`.
### Float API (Double)
#### `fiobj_float_new`
```c
FIOBJ fiobj_float_new(double num);
```
Creates a Float object. Remember to use `fiobj_free`.
#### `fiobj_float_set`
```c
void fiobj_float_set(FIOBJ obj, double num);
```
Mutates a Float object's value. Effects every object's reference!
#### `fiobj_float_tmp`
```c
FIOBJ fiobj_float_tmp(double num);
```
Creates a temporary Float object. Avoid using `fiobj_free`.
### Numerical Helpers: not FIOBJ specific, but included as part of the library
#### `fio_atol`
```c
int64_t fio_atol(char **pstr);
```
A helper function that converts between String data to a signed int64_t.
Numbers are assumed to be in base 10.
The `0x##` (or `x##`) and `0b##` (or `b##`) are recognized as base 16 and
base 2 (binary MSB first) respectively.
The pointer will be updated to point to the first byte after the number.
#### `fio_atof`
```c
double fio_atof(char **pstr);
```
A helper function that converts between String data to a signed double.
#### `fio_ltoa`
```c
size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
```
A helper function that converts between a signed int64_t to a string.
No overflow guard is provided, make sure there's at least 66 bytes available
(for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL terminator).
#### `fio_ftoa`
```c
size_t fio_ftoa(char *dest, double num, uint8_t base);
```
A helper function that converts between a double to a string.
No overflow guard is provided, make sure there's at least 130 bytes available
(for base 2).
Supports base 2, base 10 and base 16. An unsupported base will silently
default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
beginning of the string).
Returns the number of bytes actually written (excluding the NUL terminator).
#### `fio_ltocstr`
```c
fio_cstr_s fio_ltocstr(long);
```
Converts a number to a temporary, thread local, C string object
The `fio_str_s` object is only valid until the function is called again within the same thread or the thread terminates.
#### `fio_ftocstr`
```c
fio_cstr_s fio_ftocstr(double);
```
Converts a float to a temporary, thread local, C string object
The `fio_str_s` object is only valid until the function is called again within the same thread or the thread terminates.
# FIOBJ String API
## Overview
This documentation is incomplete. I would love your help to finish it up. Until that time, please read the documentation in [the `fiobj_str.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fiobj_str.h).
# FIOBJ Array API
### Array creation API
#### `fiobj_ary_new`
```c
FIOBJ fiobj_ary_new(void);
```
Creates a mutable empty Array object. Use `fiobj_free` when done.
#### `fiobj_ary_new2`
```c
FIOBJ fiobj_ary_new2(size_t capa);
```
Creates a mutable empty Array object with the requested capacity.
### Accessing Array Members / Data
#### `fiobj_ary_count`
```c
size_t fiobj_ary_count(FIOBJ ary);
```
Returns the number of elements in the Array.
#### `fiobj_ary_capa`
```c
size_t fiobj_ary_capa(FIOBJ ary);
```
Returns the current, temporary, array capacity (it's dynamic).
#### `fiobj_ary2ptr`
```c
FIOBJ *fiobj_ary2ptr(FIOBJ ary);
```
Returns a TEMPORARY pointer to the beginning of the array.
This pointer can be used for sorting and other direct access operations as long as no other actions (insertion/deletion) are performed on the array.
#### `fiobj_ary_index`
```c
FIOBJ fiobj_ary_index(FIOBJ ary, int64_t pos);
```
Returns a temporary object owned by the Array.
Wrap this function call within `fiobj_dup` to get a persistent handle. i.e.:
```c
fiobj_dup(fiobj_ary_index(array, 0));
```
Negative values are retrieved from the end of the array. i.e., `-1` is the last item.
/** */
/**
Sets an object at the requested position.
/
void fiobj_ary_set(FIOBJ ary, FIOBJ obj, int64_t pos);
#### `fiobj_ary_entry`
```c
#define fiobj_ary_entry(a, p) fiobj_ary_index((a), (p))
```
Alias for [`fiobj_ary_index`](#fiobj_ary_index).
### Array push / shift API
#### `fiobj_ary_push`
```c
void fiobj_ary_push(FIOBJ ary, FIOBJ obj);
```
Pushes an object to the end of the Array.
#### `fiobj_ary_pop`
```c
FIOBJ fiobj_ary_pop(FIOBJ ary);
```
Pops an object from the end of the Array.
#### `fiobj_ary_unshift`
```c
void fiobj_ary_unshift(FIOBJ ary, FIOBJ obj);
```
Unshifts an object to the beginning of the Array. This could be expensive.
#### `fiobj_ary_shift`
```c
FIOBJ fiobj_ary_shift(FIOBJ ary);
```
Shifts an object from the beginning of the Array.
# Find / Remove / Replace
#### `fiobj_ary_replace`
```c
FIOBJ fiobj_ary_replace(FIOBJ ary, FIOBJ obj, int64_t pos);
```
Replaces the object at a specific position, returning the old object -
remember to `fiobj_free` the old object.
#### `fiobj_ary_find`
```c
int64_t fiobj_ary_find(FIOBJ ary, FIOBJ data);
```
Finds the index of a specifide object (if any). Returns -1 if the object
isn't found.
#### `fiobj_ary_remove`
```c
int fiobj_ary_remove(FIOBJ ary, int64_t pos);
```
Removes the object at the index (if valid), changing the index of any
following objects.
Returns 0 on success or -1 (if no object or out of bounds).
#### `fiobj_ary_remove2`
```c
int fiobj_ary_remove2(FIOBJ ary, FIOBJ data);
```
Removes the first instance of an object from the Array (if any), changing the
index of any following objects.
Returns 0 on success or -1 (if the object wasn't found).
### Array Compacting
#### `fiobj_ary_compact`
```c
void fiobj_ary_compact(FIOBJ ary);
```
Removes any NULL *pointers* from an Array, keeping all Objects (including
explicit NULL objects) in the array.
This action is O(n) where n in the length of the array.
It could get expensive.
# FIOBJ Hash Map API
### Hash Creation
#### `fiobj_hash_new`
```c
FIOBJ fiobj_hash_new(void);
```
Creates a mutable empty Hash object. Use `fiobj_free` when done.
Notice that these Hash objects are optimized for smaller collections and retain order of object insertion.
#### `fiobj_hash_new2`
```c
FIOBJ fiobj_hash_new2(size_t capa);
```
Creates a mutable empty Hash object with an initial capacity of `capa`. Use `fiobj_free` when done.
This allows optimizations for larger (or smaller) collections.
### Hash properties and state
#### `fiobj_hash_capa`
```c
size_t fiobj_hash_capa(const FIOBJ hash);
```
Returns a temporary theoretical Hash map capacity. This could be used for testing performance and memory consumption.
#### `fiobj_hash_count`
```c
size_t fiobj_hash_count(const FIOBJ hash);
```
Returns the number of elements in the Hash.
### Populating and Managing the Hash
#### `fiobj_hash_set`
```c
int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj);
```
Sets a key-value pair in the Hash, duplicating the Symbol and **moving** the ownership of the object to the Hash.
Returns -1 on error.
#### `fiobj_hash_pop`
```c
FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key);
```
Allows the Hash to be used as a stack.
If a pointer `key` is provided, it will receive ownership of the key (remember to free).
Returns FIOBJ_INVALID on error.
Returns and object if successful (remember to free).
#### `fiobj_hash_replace`
```c
FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj);
```
Replaces the value in a key-value pair, returning the old value (and it's ownership) to the caller.
A return value of FIOBJ_INVALID indicates that no previous object existed (but a new key-value pair was created.
Errors are silently ignored.
Remember to free the returned object.
#### `fiobj_hash_remove`
```c
FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key);
```
Removes a key-value pair from the Hash, if it exists, returning the old object (instead of freeing it).
#### `fiobj_hash_remove2`
```c
FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t key_hash);
```
Removes a key-value pair from the Hash, if it exists, returning the old object (instead of freeing it).
#### `fiobj_hash_delete`
```c
int fiobj_hash_delete(FIOBJ hash, FIOBJ key);
```
Deletes a key-value pair from the Hash, if it exists, freeing the associated object.
Returns -1 on type error or if the object never existed.
#### `fiobj_hash_delete2`
```c
int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash);
```
Deletes a key-value pair from the Hash, if it exists, freeing the associated object.
This function takes a `uint64_t` Hash value (see `fio_siphash`) to perform a lookup in the HashMap, which is slightly faster than the other variations.
Returns -1 on type error or if the object never existed.
#### `fiobj_hash_get`
```c
FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key);
```
Returns a temporary handle to the object associated with the Symbol, FIOBJ_INVALID if none.
#### `fiobj_hash_get2`
```c
FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash);
```
Returns a temporary handle to the object associated hashed key value.
This function takes a `uint64_t` Hash value (see `fio_siphash`) to
perform a lookup in the HashMap, which is slightly faster than the other
variations.
Returns FIOBJ_INVALID if no object is associated with this hashed key value.
#### `fiobj_hash_haskey`
```c
int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key);
```
Returns 1 if the key (Symbol) exists in the Hash, even if it's value is NULL.
#### `fiobj_hash_clear`
```c
void fiobj_hash_clear(const FIOBJ hash);
```
Empties the Hash.
### Rehashing
Rehashing is usually performed automatically, as needed, and shouldn't be performed manually unless there is a known reason to do so.
#### `fiobj_hash_rehash`
```c
void fiobj_hash_rehash(FIOBJ h);
```
Attempts to rehash the Hash Map.
# JSON API
## Overview
Parsing, editing and outputting JSON in C can be easily accomplished using [facil.io's dynamic types](https://facil.io/0.6.x/fiobj/) (`FIOBJ`).
There are [faster alternatives as well as slower alternatives out there](json_performance.html) (i.e., the Qajson4c library is probably the most balanced alternative).
However, `facil.io` offers the added benefit of complete parsing from JSON to object. This is in contrast to some parsers that offer a mid-way structure (often a linked list of JSON nodes) or lazy (delayed) parsing for types such as `true`, `false` and Numbers.
`facil.io` also offers the added benefit of complete formatting from a framework wide object type (`FIOBJ`) to JSON. This is in contrast to some solutions that require a linked list of node structures.
### Example
The `facil.io` parser will parse any C string until it either consumes the whole string or completes parsing of a JSON object.
For example, the following program will minify (or prettify) JSON data:
```c
// include the `fiobj` module from `facil.io`
#include "fiobj.h"
#include
// this is passed as an argument to `fiobj_obj2json`
// change this to 1 to prettify.
#define PRETTY 1
int main(int argc, char const *argv[]) {
// a default string to demo
const char *json = u8"{\n\t\"id\":1,\n"
"\t// comments are ignored.\n"
"\t\"number\":42,\n"
"\t\"float\":42.42,\n"
"\t\"string\":\"\\uD834\\uDD1E oh yeah...\",\n"
"\t\"hash\":{\n"
"\t\t\"nested\":true\n"
"\t},\n"
"\t\"symbols\":[\"id\","
"\"number\",\"float\",\"string\",\"hash\",\"symbols\"]\n}";
if (argc == 2) {
// accept command line JSON data
json = argv[1];
}
printf("\nattempting to parse:\n%s\n", json);
// Parsing the JSON
FIOBJ obj = FIOBJ_INVALID;
size_t consumed = fiobj_json2obj(&obj, json, strlen(json));
// test for errors
if (!consumed || !obj) {
printf("\nERROR, couldn't parse data.\n");
exit(-1);
}
// Example use - printing some JSON data
FIOBJ key = fiobj_str_new("number", 6);
if (FIOBJ_TYPE_IS(obj, FIOBJ_T_HASH) // make sure the JSON object is a Hash
&& fiobj_hash_get(obj, key)) { // test for the existence of "number"
printf("JSON print example - the meaning of life is %zu",
(size_t)fiobj_obj2num(fiobj_hash_get(obj, key)));
}
fiobj_free(key);
// Formatting the JSON back to a String object and printing it up
FIOBJ str = fiobj_obj2json(obj, PRETTY);
printf("\nParsed JSON input was: %zu bytes"
"\nJSON output is %zu bytes:\n\n%s\n\n",
consumed, (size_t)fiobj_obj2cstr(str).len, fiobj_obj2cstr(str).data);
// cleanup
fiobj_free(str);
fiobj_free(obj);
return 0;
}
```
## Constants
`JSON_MAX_DEPTH` is the maximum depth for nesting. The default value is 32 (should be set during compile time).
Since bit mapping is used, the maximum available nesting value is 32 (32 nested levels use 32 bits in a `uint32_t` type).
Note that facil.io avoids recursion to protect against DoS attacks that attempt stack exploding techniques.
## Types
### Parsing result types
The JSON parser returns a `FIOBJ` dynamic object of any type (depending on the JSON data).
No assumptions should be made
### Formating result type
The JSON formatter returns a `FIOBJ` dynamic String, always.
## Functions
### `fiobj_json2obj`
```c
size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
```
Parses JSON, setting `pobj` to point to the new Object.
Returns the number of bytes consumed. On Error, 0 is returned and no data is consumed.
### `fiobj_obj2json`
```c
FIOBJ fiobj_obj2json(FIOBJ, uint8_t pretty);
```
Stringify an object into a JSON string. Remember to `fiobj_free`.
Note that only the following basic fiobj types are supported: Primitives (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
Some objects (such as the POSIX specific IO type) are unsupported and may be formatted incorrectly.
### `fiobj_obj2json2`
```c
FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ object, uint8_t pretty);
```
Formats an object into a JSON string, appending the JSON string to an existing String. Remember to `fiobj_free` as usual.
Note that only the foloowing basic fiobj types are supported: Primitives (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
Some objects (such as the POSIX specific IO type) are unsupported and may be formatted incorrectly.
## Important Notes
The parser assumes the whole JSON data is present in the data's buffer. A streaming parser is coded into the [`fiobj_json.c` source file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fiobj_json.c) but no external API is exposed.
The [`fiobj_json.h` header file](https://github.com/boazsegev/facil.io/blob/master/lib/facil/core/types/fiobj/fiobj_json.h) might include more data.
# Simple Dynamic Arrays
The simple dynamic array offers a simple data structure that manages only it's own memory (but not the memory of any objects placed in the array).
## Overview
The simple dynamic array type is included in a single file library, `fio_ary.h` that can be used independently as well.
The Core Array is designed to contain `void *` pointers and doesn't manage any memory except it's own.
As a general rule, facil.io doen's make assumptions about allocation / deallocation except where mentioned. A good example is the `sock_write` function that copies the data vs. `sock_write2` which asks about the correct deallocation function to be used (defaults to `free`) and takes ownership of the data.
## Example
Here's a short example from [the introduction to the simple core types](https://facil.io/0.6.x/types/):
```c
#include "fio_ary.h"
int main(void) {
// The array container.
fio_ary_s ary;
// allocate and initialize internal data
fio_ary_new(&ary, 0);
// perform some actions
fio_ary_push(&ary, (void *)1);
FIO_ARY_FOR(&ary, pos) {
printf("index: %lu == %lu\n", (unsigned long)pos.i, (unsigned long)pos.obj);
}
// free the array's data to avoid memory leaks (doesn't free the objects)
fio_ary_free(&ary);
}
```
Note that the Array container can be placed on the stack (as well as allocated using `malloc`), but the internal data must be allocated and deallocated using `fio_ary_new` and `fio_ary_free`.
## Types
The Core Array uses the `fio_ary_s` type.
The data in in the `fio_ary_s` type shouldn't be accessed directly. Functional access should be preferred.
```c
typedef struct fio_ary_s {
size_t start;
size_t end;
size_t capa;
void **arry;
} fio_ary_s;
```
## Functions
### The `fio_ary_new` function
```c
void fio_ary_new(fio_ary_s *ary, size_t capa)
```
Initializes the array and allocates memory for it's internal data.
Note that `capa` indicates the **initial** (or *minimal*) capacity for the array, but the array can grow as long as memory allows.
This will assume that `ary` is uninitialized and overwrite any existing data.
### the `fio_ary_free` function
```c
void fio_ary_free(fio_ary_s *ary)
```
Frees the array's internal data.
If the `ary`'s container (the `fio_ary_s` object) was allocated using `malloc`, a subsequent call to `free(ary)` must be made.
### the `fio_ary_count` function
```c
size_t fio_ary_count(fio_ary_s *ary)
```
Returns the number of elements in the Array.
### the `fio_ary_capa` function
```c
size_t fio_ary_capa(fio_ary_s *ary)
```
Returns the current, temporary, array capacity (it's dynamic).
### the `fio_ary_index` function
```c
void *fio_ary_index(fio_ary_s *ary, int64_t pos)
```
Returns the object placed in the Array, if any. Returns NULL if no data or if
the index is out of bounds.
Negative values are retrived from the end of the array. i.e., `-1`
is the last item.
`fio_ary_entry` is an alias for `fiobj_ary_index`.
### the `fio_ary_set` function
```c
void *fio_ary_set(fio_ary_s *ary, void *data, int64_t pos)
```
Sets an object at the requested position.
Returns the old value, if any.
If an error occurs, the same data passed to the function is returned (test using `fiobj_ary_index`).
### the `fio_ary_push` function
```c
int fio_ary_push(fio_ary_s *ary, void *data)
```
Pushes an object to the end of the Array. Returns -1 on error.
### the `fio_ary_pop` function
```c
void *fio_ary_pop(fio_ary_s *ary)
```
Pops an object from the end of the Array
Returns NULL if the object was NULL or the Array was empty.
### the `fio_ary_unshift` function
```c
int fio_ary_unshift(fio_ary_s *ary, void *data)
```
Unshifts an object to the beginning of the Array. Returns -1 on error.
This could be expensive, causing `memmove`.
### the `fio_ary_shift` function
```c
void *fio_ary_shift(fio_ary_s *ary)
```
Shifts an object from the beginning of the Array.
Returns NULL if the object was NULL or the Array was empty.
### the `fio_ary_compact` function
```c
void fio_ary_compact(fio_ary_s *ary)
```
Removes any NULL *pointers* from an Array, keeping all other data in the
array.
This action is O(n) where n in the length of the array.
### the `FIO_ARY_FOR` macro
```c
FIO_ARY_FOR(ary, pos)
```
Iterates through the list using a `for` loop.
Access the data with `pos.obj` and it's index with `pos.i`.
The `pos` variable can be named however you please (i.e. `FIO_ARY_FOR(&bar, foo)` for `foo.i` and `foo.obj`).
### the `fio_ary_each` function
```c
size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
int (*task)(void *pt, void *arg),
void *arg);
```
Iteration using a callback for each entry in the array.
The callback task function must accept an the entry data as well as an opaque
user pointer.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
# A Simple Hash Map
## Overview
The Hash Map core type offers an ordered key-value map with a very simple API.
The Hash Map is ordered by order of insertion.
The core Hash Map type is provided in a single file library `fio_hashmap.h`, which allows it's functions to be inlined for maximum performance (similarly to macros).
The Hash Map defaults to `uint64_t` keys, meaning that matching is performed using a simple integer comparison. However, [this could be changed by defining macros **before** including the library file for increased collision protection](#collision-protection).
Much like the Array in [the introduction to the simple core types](https://facil.io/0.6.x/types/), Hash Map containers can be placed on the stack as well as allocated dynamically.
## Types
The Core Hash uses the `fio_hash_s` type.
The data in in the `fio_hash_s` type should be considered *opaque* and shouldn't be accessed directly. Functional access should be preferred.
The `fio_hash_s` utilizes two arrays using internal data types that contain duplicates of the Hash key data, maximizing memory locality for Hash Map operations and minimizing cache misses to increase performance.
```c
typedef struct fio_hash_data_ordered_s {
FIO_HASH_KEY_TYPE key; /* another copy for memory cache locality */
void *obj;
} fio_hash_data_ordered_s;
typedef struct fio_hash_data_s {
FIO_HASH_KEY_TYPE key; /* another copy for memory cache locality */
struct fio_hash_data_ordered_s *obj;
} fio_hash_data_s;
/* the information in the Hash Map structure should be considered READ ONLY. */
struct fio_hash_s {
uintptr_t count;
uintptr_t capa;
uintptr_t pos;
uintptr_t mask;
fio_hash_data_ordered_s *ordered;
fio_hash_data_s *map;
};
```
## Functions
### `fio_hash_new`
```c
void fio_hash_new(fio_hash_s *hash)
```
Allocates and initializes internal data and resources.
It's important to call this function before using the Hash Map.
Lazy initialization of the Hash Map is possible by initializing the `fio_hash_s` to 0 (the default for static variables).
### `fio_hash_free`
```c
void fio_hash_free(fio_hash_s *hash)
```
Deallocates any internal resources.
It is critical that this function is called to prevent memory leaks. **However**, this function is not enough to prevent memory leaks - custom keys (if any) and object data should also be deallocated (see also `FIO_HASH_FOR_FREE`).
### `fio_hash_insert`
```c
void *fio_hash_insert(fio_hash_s *hash,
FIO_HASH_KEY_TYPE key,
void *obj)`
```
Inserts an object to the Hash Map Table, rehashing if required, returning the old object if it exists.
Set `obj` to NULL to remove an existing object (the existing object will be returned).
The `FIO_HASH_KEY_TYPE` defaults to `uint64_t`, [this could be changed by defining macros **before** including the library file for increased collision protection](#collision-protection).
### `fio_hash_find`
```c
void *fio_hash_find(fio_hash_s *hash, FIO_HASH_KEY_TYPE key)
```
Locates an object in the Hash Map Table according to the hash key value.
### `fio_hash_count`
```c
size_t fio_hash_count(const fio_hash_s *hash)
```
Returns the number of elements currently in the Hash Table.
### `fio_hash_capa`
```c
size_t fio_hash_capa(const fio_hash_s *hash)
```
Returns a temporary theoretical Hash map capacity.
This could be used for testing performance and memory consumption.
### `fio_hash_compact`
```c
size_t fio_hash_compact(fio_hash_s *hash)
```
Attempts to minimize memory usage by removing empty spaces caused by deleted
items (freeing their custom keys, if any) and rehashing the Hash Map.
Returns the updated hash map capacity.
### `fio_hash_rehash`
```c
void fio_hash_rehash(fio_hash_s *hash)
```
Forces a rehashing of the hash, increasing memory consumption as well as minimizing internal collisions (possibly improving seek times).
This function is called automatically when needed, it's unlikely that you should want to call the function yourself.
### `fio_hash_each`
```c
size_t fio_hash_each(fio_hash_s *hash,
const size_t start_at,
int (*task)(FIO_HASH_KEY_TYPE key,
void *obj,
void *arg),
void *arg);
```
Iteration using a callback for each entry in the Hash Table.
The callback task function must accept the hash key, the entry data and an
opaque user pointer:
```c
int example_task(FIO_HASH_KEY_TYPE key, void *obj, void *arg) {return 0;}
```
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the relative "stop" position, i.e., the number of items processed +
the starting point.
### `FIO_HASH_FOR_LOOP`
```c
FIO_HASH_FOR_LOOP(hash, i)
```
A macro for a `for` loop that iterates over all the hashed objects (in
order).
`hash` a pointer to the hash table variable and `i` is a temporary variable
name to be created for iteration.
`i->key` is the key and `i->obj` is the hashed data.
The `i` variable can be names differently (i.e. `FIO_HASH_FOR_LOOP(hash, pos)` for `pos->key` and `pos->obj`).
### `FIO_HASH_FOR_FREE`
```c
FIO_HASH_FOR_FREE(hash, i)
```
A macro for a `for` loop that will iterate over all the hashed objects (in
order) and empties the hash, later calling `fio_hash_free` to free the hash.
`hash` a pointer to the hash table variable and `i` is a temporary variable
name to be created for iteration.
`i->key` is the key and `i->obj` is the hashed data.
Free the objects and the Hash Map container manually (if required). Custom keys will be freed automatically when using this macro.
### `FIO_HASH_FOR_EMPTY`
```c
FIO_HASH_FOR_EMPTY(hash, i)
```
A macro for a `for` loop that iterates over all the hashed objects (in
order) and empties the hash.
This will also reallocate the map's memory (to zero out the data), so if this
is performed before calling `fio_hash_free`, use `FIO_HASH_FOR_FREE` instead.
`hash` a pointer to the hash table variable and `i` is a temporary variable
name to be created for iteration.
`i->key` is the key and `i->obj` is the hashed data.
Free the objects and the Hash Map container manually (if required). Custom keys will be freed automatically when using this macro.
## Collision protection
The Hash Map is collision resistant as long as it's keys are truly unique.
If there's a chance that the default `uint64_t` key type will not be be able to uniquely identify a key, the following macros should **all** be defined, **before** including the `fio_hashmap.h` file in the `.c` file, allowing the default key system to be replaced:
### `FIO_HASH_KEY_TYPE`
This macro sets the type used for keys.
### `FIO_HASH_KEY_INVALID`
Empty slots in the Hash Map are initialized so all their bytes are zero.
This macro should signify a static key that has all it's byte set to zero, making it an invalid key (it cannot be used, and objects placed in that slot will be lost).
### `FIO_HASH_KEY2UINT(key)`
This macro should convert the key to a unique unsigned number.
This, in effect, should return the hash value for the key and cannot be zero.
The value is used to determine the location of the key in the map (prior to any collisions) and a good hash will minimize collisions.
### `FIO_HASH_COMPARE_KEYS(k1, k2)`
This macro should compare two keys, excluding their hash values (which were compared using the `FIO_HASH_KEY2UINT` macro).
### `FIO_HASH_KEY_ISINVALID(key)`
Should evaluate as true if the key is an invalid key (all it's bytes set to zero).
### `FIO_HASH_KEY_COPY(key)`
Keys might contain temporary data (such as strings). To allow the Hash Map to test the key even after the temporary data is out of scope, ac opy needs to be created.
This macro should return a FIO_HASH_KEY_TYPE object that contains only persistent data. This could achieved by allocating some of the data using `malloc`.
### `FIO_HASH_KEY_DESTROY(key)`
When the Hash Map is re-hashed, old keys belonging to removed objects are cleared away and need to be destroyed.
This macro allows dynamically allocated memory to be freed (this is the complement of `FIO_HASH_KEY_COPY`).
**Note**: This macro must not end a statement (shouldn't use the `;` marker) or code blocks (`{}`). For multiple actions consider using inline functions.
### Notes
If the `FIO_HASH_KEY_COPY` macro allocated memory dynamically or if there's a need to iterate over the values in the Hash Map before freeing the Hash Map (perhaps to free the object's memory), the `FIO_HASH_FOR_FREE` macro can be used to iterate over the Hash Map, free all the keys and free the Hash Map resources (it ends by calling `fio_hash_free`).
**Note**: These macros are localized to the specific C file in which they were defined and a specific C file can't include the `fio_hashmap.h` more than once. If a few different approaches are required, it can be performed by using different C files and offering function wrappers for the Hash Map functions (wrap `fio_hash_find`, `fio_hash_insert`, etc').
### Example
In this example collisions are forced by setting the `hash` and string length to be equal for all keys, this demonstrates how defining these macros can secure the hash against String key collisions.
(another example can be found in the `pubsub.c` source code)
```c
#define _GNU_SOURCE
#include
#include
#include
/* the hash key type for string keys */
typedef struct {
size_t hash;
size_t len;
char *str;
} fio_hash_key_s;
/* strdup is usually available... but just in case it isn't */
static inline char *my_strdup(char *str, size_t len) {
char *ret = malloc(len + 1);
if(!ret)
exit(-1);
ret[len] = 0;
memcpy(ret, str, len);
return ret;
}
/* define the macro to set the key type */
#define FIO_HASH_KEY_TYPE fio_hash_key_s
/* the macro that returns the key's hash value */
#define FIO_HASH_KEY2UINT(key) ((key).hash)
/* Compare the keys using length testing and `memcmp` */
#define FIO_HASH_COMPARE_KEYS(k1, k2) \
((k1).str == (k2).str || \
((k1).len == (k2).len && !memcmp((k1).str, (k2).str, (k2).len)))
/* an "all bytes are zero" invalid key */
#define FIO_HASH_KEY_INVALID ((fio_hash_key_s){.hash = 0})
/* tests if a key is the invalid key */
#define FIO_HASH_KEY_ISINVALID(key) ((key).str == NULL)
/* creates a persistent copy of the key's string */
#define FIO_HASH_KEY_COPY(key) \
((fio_hash_key_s){.hash = (key).hash, \
.len = (key).len, \
.str = my_strdup((key).str, (key).len)})
/* frees the allocated string, remove the `fprintf` in production */
#define FIO_HASH_KEY_DESTROY(key) \
(fprintf(stderr, "freeing %s\n", (key).str), free((key).str))
#include "fio_hashmap.h"
int main(void) {
fio_hash_s hash;
fio_hash_key_s key1 = {.hash = 1, .len = 5, .str = "hello"};
fio_hash_key_s key1_copy = {.hash = 1, .len = 5, .str = "hello"};
fio_hash_key_s key2 = {.hash = 1, .len = 5, .str = "Hello"};
fio_hash_key_s key3 = {.hash = 1, .len = 5, .str = "Hell0"};
fio_hash_new(&hash);
fio_hash_insert(&hash, key1, key1.str);
key1.str = "oops";
if (fio_hash_find(&hash, key1))
fprintf(stderr,
"ERROR: string comparison should have failed, instead got: %s\n",
(char *)fio_hash_find(&hash, key1));
else if (fio_hash_find(&hash, key1_copy))
fprintf(stderr, "Hash string comparison passed for %s\n",
(char *)fio_hash_find(&hash, key1_copy));
fio_hash_insert(&hash, key2, key2.str);
fio_hash_insert(&hash, key3, key3.str);
fio_hash_insert(&hash, key2, NULL); /* deletes the key2 object */
fio_hash_rehash(&hash); /* forces the unused key to be destroyed */
fprintf(stderr, "Did we free %s?\n", key2.str);
FIO_HASH_FOR_EMPTY(&hash, i) { (void)i->obj; }
if (fio_hash_find(&hash, key1_copy))
fprintf(stderr,
"ERROR: string comparison should have failed, instead got: %s\n",
(char *)fio_hash_find(&hash, key1));
fprintf(stderr, "reinserting stuff\n");
fio_hash_insert(&hash, key2, key2.str);
fio_hash_insert(&hash, key3, key3.str);
FIO_HASH_FOR_FREE(&hash, i) { (void)i->obj; }
}
```
# A Simple Linked List
Linked lists come in two main flavors:
1. Generic (or independent) Linked Lists:
Where the list's node structure has a pointer that points to the object being added to the list. This requires objects to be allocated separately from list node, but allows a single object to belong to many lists (one-to-many).
2. Embedded Linked Lists:
Where the list's node structure needs to be embedded within the object, thereby minimizing memory allocation and improving memory locality, while creating a one-to-one restriction (one object can belong to one list).
The single file library `fio_llist.h` offers both flavors in a simple to use package.
The API is practically the same for both, the only difference is in the prefix (`fio_ls_X` vs. `fio_ls_embd_X`).
## Data Structure and Initialization.
The container for a list is the same as a node, however, it must be initialized so that it links to itself (see `FIO_LS_INIT`). i.e.:
```c
fio_ls_s my_list = FIO_LS_INIT(my_list);
```
The linked list container is a simple data structure shown here... however, it is best to **access the data using the API and avoid accessing the data directly**. This will both protect the program from future changes to the data structures and minimize possible errors.
```c
/** an embeded linked list. */
typedef struct fio_ls_embd_s {
struct fio_ls_embd_s *prev;
struct fio_ls_embd_s *next;
} fio_ls_embd_s;
/** an independent linked list. */
typedef struct fio_ls_s {
struct fio_ls_s *prev;
struct fio_ls_s *next;
const void *obj;
} fio_ls_s;
#define FIO_LS_INIT(name) { .next = &(name), .prev = &(name) }
```
The container can be dynamically allocated, placed of the stack or embedded in a dynamic object.
## Generic Linked List API
Note that the API is comprised of **inline static functions that act like macros**, so there is no performance to be gained by accessing the data structure directly (only integrity to be lost).
### `fio_ls_push`
```c
void fio_ls_push(fio_ls_s *pos, const void *obj)
```
Adds an object to the list's head.
### `fio_ls_unshift`
```c
void fio_ls_unshift(fio_ls_s *pos, const void *obj)
```
Adds an object to the list's tail.
### `fio_ls_pop`
```c
void *fio_ls_pop(fio_ls_s *list)
```
Removes an object from the list's head.
### `fio_ls_shift`
```c
void *fio_ls_shift(fio_ls_s *list)
```
Removes an object from the list's tail.
### `fio_ls_remove`
```c
void *fio_ls_remove(fio_ls_s *node)
```
Removes a node from the list, returning the contained object.
### `fio_ls_is_empty`
```c
int fio_ls_is_empty(fio_ls_s *list)
```
Tests if the list is empty.
### `fio_ls_any`
```c
int fio_ls_any(fio_ls_s *list)
```
Tests if the list is NOT empty (contains any nodes).
### `FIO_LS_FOR`
```c
FIO_LS_FOR(list, pos)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you please).
## Embedded List API
### `fio_ls_embd_push`
```c
void fio_ls_embd_push(fio_ls_embd_s *dest, fio_ls_embd_s *node)
```
Adds a node to the list's head.
### `fio_ls_embd_unshift`
```c
fio_ls_embd_unshift(fio_ls_embd_s *dest, fio_ls_embd_s *node)
```
Adds a node to the list's tail.
### `fio_ls_embd_pop`
```c
fio_ls_embd_s *fio_ls_embd_pop(fio_ls_embd_s *list)
```
Removes a node from the list's head.
### `fio_ls_embd_shift`
```c
fio_ls_embd_s *fio_ls_embd_shift(fio_ls_embd_s *list)
```
Removes a node from the list's tail.
### `fio_ls_embd_remove`
```c
fio_ls_embd_s *fio_ls_embd_remove(fio_ls_embd_s *node)
```
Removes a node from it's container list.
### `fio_ls_embd_is_empty`
```c
fio_ls_embd_is_empty(fio_ls_embd_s *list)
```
Tests if the list is empty.
### `fio_ls_embd_any`
```c
int fio_ls_embd_any(fio_ls_embd_s *list)
```
Tests if the list is NOT empty (contains any nodes).
### `FIO_LS_EMBD_FOR`
```c
FIO_LS_EMBD_FOR(list, node)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you pleas..
### `FIO_LS_EMBD_OBJ`
```c
FIO_LS_EMBD_OBJ(type, member, plist) \
((type *)((uintptr_t)(plist) - (uintptr_t)(&(((type *)0)->member))))
```
Takes a list pointer `plist` and returns a pointer to it's container.
This uses pointer offset calculations and can be used to calculate any struct's pointer (not just list containers) as an offset from a pointer of one of it's members.
This is a very useful macro to have around.
# API
Although the facil.io includes extensive documentation within the source file, it's often helpful to have an overview the explains about a framework's idioms and general approach.
This is why, along side the actual API data, you will find explanations about facil.io's approach to memory "ownership" and similar topics that are consistent across the whole of the API.
The API sections are divided into:
* [Core Types API](https://facil.io/0.6.x/types/), which includes explanations about the facil.io dynamic object types (`FIOBJ`), Hash Maps, Dynamic Arrays and Linked Lists.
* [Modules API](https://facil.io/0.6.x/modules/), which includes explanations about the core `facil` library and the built-in modules and extensions, such as the CLI helper, the HTTP/Websocket protocols, the Pub/Sub engines etc'.
### Your support is appreciated
facil.io started as a pet project. However, it seems facil.io grew to be bigger than I anticipated.
I am just one person and I have a limited amount of time to invest... which is why the documentation on this website is incomplete.
If you could help me copy the documentation from the source files and format it into markdown, it would be a great benefit for everyone using the facil.io framework.
It would also free my time so I could actually code more features and improve the existing code-base. After all, when I'm writing documentation I'm not coding.
Just visit the [GitHub repository](https://github.com/boazsegev/facil.io), fork and submit your PR. The documentation is all in the [`/docs` folder](https://github.com/boazsegev/facil.io/tree/master/docs) (which gets converted to HTML and becomes this beautiful website).
# FIOBJ Generic Functions
Some `FIOBJ` functions can be called for any `FIOBJ` object, regardless of their type.
These generic functions are listed here.
### FIOBJ Memory Management
#### `fiobj_dup`
```c
FIOBJ fiobj_dup(FIOBJ);
```
Heuristic copy with a preference for copy by reference(!), to minimize allocations.
This usually means the only action performed is reference count increase.
Always returns the value passed along.
#### `fiobj_free`
```c
void fiobj_free(FIOBJ);
```
Frees the object and any of it's "children".
This function affects nested objects, meaning that when an Array or a Hash object is passed along, it's children (the nested objects) are also freed.
For Hash objects, only the value objects are deallocated. The `keys` aren't "owned" by the Hash Map and therefore they aren't automatically allocated.
### FIOBJ Soft Type Recognition
#### `fiobj_type`
```c
fiobj_type_enum fiobj_type(FIOBJ o);
```
Returns the object's type.
Valid return values are:
`FIOBJ_T_NULL` - Object is the primitive `null`
* `FIOBJ_T_TRUE` - Object is the primitive `true`
* `FIOBJ_T_FALSE` - Object is the primitive `false`
* `FIOBJ_T_NUMBER` - Object is a number.
* `FIOBJ_T_FLOAT` - Object is a floating point number (`double`).
* `FIOBJ_T_STRING` - Object is a binary String.
* `FIOBJ_T_ARRAY` - Object is a FIOBJ array.
* `FIOBJ_T_HASH` - Object is a FIOBJ hash.
* `FIOBJ_T_DATA` - Object is a data stream, either wrapping a temporary file or a memory block.
* `FIOBJ_T_UNKNOWN` - Object type is unknown (a user's type).
#### `fiobj_type_is`
```c
size_t fiobj_type_is(FIOBJ o, fiobj_type_enum type)
```
This is faster than getting the type, since parts of the switch statement are optimized away (they are calculated during compile time).
#### `fiobj_type_name`
```c
const char *fiobj_type_name(const FIOBJ obj);
```
Returns a C string naming the objects dynamic type.
### Object Equality / Truthfulness
#### `fiobj_is_true`
```c
int fiobj_is_true(const FIOBJ);
```
Tests if an object evaluates as TRUE.
This is object type specific. For example, empty strings might evaluate as FALSE, even though they aren't a boolean type.
#### `fiobj_iseq`
```c
int fiobj_iseq(const FIOBJ obj1, const FIOBJ obj2);
```
Deeply compares two objects. No hashing or recursive function calls are
involved.
Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
Hash objects are order sensitive. To be equal, Hash keys must match in order.
Returns 1 if true and 0 if false.
### Object Conversion
#### `fiobj_obj2num`
```c
intptr_t fiobj_obj2num(const FIOBJ obj);
```
Returns an Object's numerical value.
If a String is passed to the function, it will be parsed assuming base 10
numerical data.
Hashes and Arrays return their object count.
IO objects return the length of their data.
A type error results in 0.
#### `fiobj_obj2float`
```c
double fiobj_obj2float(const FIOBJ obj);
```
Returns a Float's value.
If a String is passed to the function, they will benparsed assuming base 10
numerical data.
A type error results in 0.
#### `fiobj_obj2cstr`
```c
fio_cstr_s fiobj_obj2cstr(const FIOBJ obj);
```
Returns a C String (NUL terminated) using the `fio_cstr_s` data type.
The Sting in binary safe and might contain NUL bytes in the middle as well as
a terminating NUL.
If a a Number or a Float are passed to the function, they
will be parsed as a *temporary*, thread-safe, String.
Numbers will be represented in base 10 numerical data.
A type error results in NULL (i.e. object can't be represented automatically as a String).
#### `fiobj_obj2hash`
```c
uint64_t fiobj_obj2hash(const FIOBJ o);
```
Calculates an Objects's SipHash value for possible use as a HashMap key.
The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In other woords, Hash Objects and Arrays can NOT be used for Hash keys.
### Iteration
#### `fiobj_each1`
```c
size_t fiobj_each1(FIOBJ, size_t start_at,
int (*task)(FIOBJ obj, void *arg), void *arg);
```
Single layer iteration using a callback for each nested fio object.
Accepts any `FIOBJ` type but only collections (Arrays and Hashes) are
processed. The container itself (the Array or the Hash) is **not** processed
(unlike `fiobj_each2`).
The callback task function must accept an object and an opaque user pointer.
Hash objects pass along only the value object. The keys can be accessed using the [`fiobj_hash_key_in_loop`](#fiobj_hash_key_in_loop) function.
If the callback returns -1, the loop is broken. Any other value is ignored.
Returns the "stop" position, i.e., the number of items processed + the
starting point.
#### `fiobj_each2`
```c
size_t fiobj_each2(FIOBJ, int (*task)(FIOBJ obj, void *arg), void *arg);
```
Deep iteration using a callback for each fio object, including the parent.
Accepts any `FIOBJ ` type.
Collections (Arrays, Hashes) are deeply probed and shouldn't be edited
during an `fiobj_each2` call (or weird things may happen).
The callback task function must accept an object and an opaque user pointer.
Hash objects keys are available using the [`fiobj_hash_key_in_loop`](#fiobj_hash_key_in_loop) function.
Notice that when passing collections to the function, the collection itself
is sent to the callback followed by it's children (if any). This is true also
for nested collections (a nested Hash will be sent first, followed by the
nested Hash's children and then followed by the rest of it's siblings.
If the callback returns -1, the loop is broken. Any other value is ignored.
#### `fiobj_hash_key_in_loop`
```c
FIOBJ fiobj_hash_key_in_loop(void);
```
Returns the key for the object in the current `fiobj_each` loop (if any).
# Dynamic Type System: facil.io objects (`FIOBJ`)
In this page you will find a general overview. For detailed API information please visit the Core or Type pages.
## Overview
`facil.io` offers a dynamic type system that makes it a breeze to mix object types together.
This dynamic type system is an independent module within the `facil.io` core and can be used separately.
The `FIOBJ` type API is divided by it's inner types (tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`):
* [Core and Generic API](https://facil.io/0.6.x/fiobj_core/)
* [Primitive Types](https://facil.io/0.6.x/fiobj_primitives/)
* [Number / Float](https://facil.io/0.6.x/fiobj_numbers/)
* [String](https://facil.io/0.6.x/fiobj_str/)
* [Array](https://facil.io/0.6.x/fiobj_ary/)
* [Hash](https://facil.io/0.6.x/fiobj_hash/)
* [Data](https://facil.io/0.6.x/fiobj_data/)
* [JSON](https://facil.io/0.6.x/fiobj_json/)
### Why we need dynamic types?
C doesn't lend itself easily to the dynamic types that are often used in languages such as Javascript. This makes it harder to use an optimized C backend (server) when the frontend (client / browser) expects multi-type responses such as JSON objects.
To resolve this difference in expectations, `facil.io` offers the `FIOBJ` type system.
This is an opaque type that can be tested using `FIOBJ_TYPE(obj)` or `FIOBJ_TYPE_IS(obj, type)`.
This offers the following advantages (among others):
* Saves you precious development time.
* Allows deep integration with `facil.io` services, reducing the need to translate from one type to another.
* Allows for "typeless" actions, such as collection iteration (`fiobj_each2`), simple conversion (`fiobj_obj2num` and `fiobj_obj2cstr`), deallocation (`fiobj_free`). reference counting (`fiobj_dup`) and equality checks (`fiobj_iseq`).
* Offers JSON parsing and formatting to and from `FIOBJ`.
* Offers non-recursive iteration.
## API Considerations
This is a short summery regarding the API and it's use. The `fiobj_*` API is well documented in the header files, so only main guidelines are mentioned.
### Functional Access
All object access should be functional, or using the macros provided. Although this requirement can be circumvented, using the functional interface should be preferred.
For example:
```c
/* this will work */
FIOBJ str = fiobj_str_buf(6); /* automatically adds room for the NUL terminator */
fio_cstr_s raw_str = fiobj_obj2cstr(str);
memcpy(raw_str.buffer, "Hello!", 6);
fiobj_str_resize(str, 6);
// ...
fiobj_free(str);
/* this is better */
FIOBJ str = fiobj_str_buf(6);
fiobj_str_write(str, "Hello!", 6);
// ...
fiobj_free(str);
/* for simple strings, one line will do */
FIOBJ str = fiobj_str_new("Hello!", 6);
// ...
fiobj_free(str);
/* for more complex cases, printf style is supported */
FIOBJ str = fiobj_strprintf("%s %d" , "Hello!", 42)
// ...
fiobj_free(str);
/* or */
FIOBJ str = fiobj_str_buf(0);
fiobj_str_write2(str, "%s %d" , "Hello!", 42);
// ...
fiobj_free(str);
/* for static strings, this is preferred */
FIOBJ str = fiobj_str_static("Hello!", 6);
// ...
fiobj_free(str);
```
### Ownership Follows Nesting
An object's memory should *always* be managed by it's "owner". This usually means the calling function.
*However*, when an object is nested within another object (i.e., placed in an Array or set as the *value* for a Hash or an HTTP header), **the ownership of the object is transferred**.
In the following example, the String nested within the Array is freed when the Array is freed:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ str = fiobj_str_new("Hello!", 6);
fiobj_ary_push(ary, str);
// ...
fiobj_free(ary);
```
Hashes follow the same rule. However...
It's important to note that **Hash keys ownership isn't transferred to the Hash** (keys are used to access and store data, but they are not the data itself).
When calling `fiobj_hash_set`, we are storing a *value* in the Hash, the *key* is what we use to access that value. This is why **the key's ownership remains with the calling function**. i.e.:
```c
FIOBJ h = fiobj_hash_new();
FIOBJ key = fiobj_str_new("life", 4);
/* By placing the Number in the Hash, it will be deallocated together with the Hash */
fiobj_hash_set(h, key, fiobj_num_new(42));
// ...
fiobj_free(h); /* Free the Hash and it's data, but NOT the key */
// ...
/* eventually we need to free the key */
fiobj_free(key);
```
### Passing By Reference
All objects are passed along by reference. The `dup` (duplication) process simply increases the reference count.
This is a very powerful tool. In the following example, `str2` is a "copy" **by reference** of `str`. By editing `str2` we're also editing `str`:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
FIOBJ str2 = fiobj_dup(str);
/* We'll edit str2 to say "Hello There!" instead of "Hello!" */
fiobj_str_resize(str2, 5);
fiobj_str_write(str2, " There!", 7);
/* This prints "Hello There!" because str was edited by reference! */
printf("%s\n", fiobj_obj2cstr(str).data);
/* we need to free both references to free the memory */
fiobj_free(str);
fiobj_free(str2);
```
An independent copy can be created using an object's specific copy function. This example create a new, independent, object instead of referencing the old one:
```c
FIOBJ str = fiobj_str_new("Hello!", 6);
/* create a copy instead of a reference */
FIOBJ str2 = fiobj_str_copy(str);
/* this is the same as */
FIOBJ str3 = fiobj_str_new(fiobj_obj2cstr(str).data, fiobj_obj2cstr(str).len);
// ...
fiobj_free(str);
fiobj_free(str2);
fiobj_free(str3);
```
Copy by reference produces a deep reference adjustment, so Arrays and Hashes can be safely copied by reference.
```c
FIOBJ ary = fiobj_ary_new();
fiobj_ary_push(ary, fiobj_str_new("Hello!", 6));
FIOBJ ary_copy = fiobj_dup(ary);
// ...
fiobj_free(ary);
// all the items in ary2 are still accessible.
fprintf(stderr, "%s\n", fiobj_obj2cstr( fiobj_ary_index(ary_copy, -1) ).buffer );
fiobj_free(ary_copy);
```
### Cyclic Nesting Errors
Cyclic protection is unsupported mostly because of performance concerns, but also because cyclic nesting is impractical for network applications (for example, how would a cyclic object be formatted into JSON?).
Cyclic nesting should be avoided. For example, the following code will crash:
```c
FIOBJ ary = fiobj_ary_new();
FIOBJ ary2 = fiobj_ary_new();
// cyclic nesting
fiobj_ary_push(ary, ary2);
fiobj_ary_push(ary2, ary);
// free might crash or produce unexpected results
fiobj_free(ary);
// each2 will cycle forever
fiobj_each2(ary2, ...);
```
## Independence
The `FIOBJ` module is independent and can be extracted from `facil.io` by copying the `fiobj.h` file (under `lib/facil/core/types`) and all the files in the `lib/facil/core/types/fiobj` folder.
Place these files in your project and use to your heart's content.
The module is licensed under the same MIT license offered by the rest of the `facil.io` source code.
# core API and extensions
facil.io's API is divided into different modules, each of them belonging to one of two groups:
* The Core Modules:
These modules are required for facil.io to operate properly and they include:
* [The `facil` library](https://facil.io/0.6.x/facil/) - this is the hub for everything network related except socket read/write calls.
the `facil` library and it's API is at the heart of everything and it is the glue that holds the different modules together.
* [The `sock` library](https://facil.io/0.6.x/sock/) - an abstraction layer over POSIX sockets, which solves many of the issues and pitfalls related to socket programming.
* [The `defer` library](https://facil.io/0.6.x/defer/) - a simple event / action queue. It's used internally but can be used directly as well.
* [The `evio` library](https://facil.io/0.6.x/evio/) - the evented *one-shot* I/O module. This module is mostly for internal uses and shouldn't be accessed directly. It's API, although stable, should be considered volatile.
* [The `FIOBJ` object library](https://facil.io/0.6.x/fiobj/) - a dynamic type system designed for handling network data (which is, by nature, dynamic).
* [The `fio_mem` library](https://facil.io/0.6.x/fio_mem/) - a simple concurrent memory allocator optimized for typical network use-cases. It can be disabled or replaced with jemalloc / tcmalloc and friends.
* The Extension Modules:
These modules can be safely ignored or removed if not utilized by an application. These modules include:
* [The `http` extension](https://facil.io/0.6.x/http/) - this is compound module is everything HTTP.
* [The `websockets` extension](https://facil.io/0.6.x/websockets/) - this is part of the HTTP module and extends it to support Websocket connections.
* [The `pubsub` extension](https://facil.io/0.6.x/pubsub/) - this allows messages to be exchanged using the [Publish–Subscribe Pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern).
This powerful tool, which integrates seamlessly with the Websocket library, can allow an application to scale horizontally across machines, as can be demonstrated by the use of the [Redis pub/sub engine](redis).
* [The `redis_engine` extension](redis) to the pub/sub service.
* [The CLI (`fio_cli`) helper](https://facil.io/0.6.x/fio_cli/) for handling command line arguments.
* [The parsers](parsers) some of the extensions, such as the Redis pub/sub engine and the HTTP / Websocket extensions make use of parsers that can be used independently of the facil.io framework.
# Facil.io - a dynamic protocol network services library
`facil.h` is "A Library (or mini-framework) for Dynamic Protocol based Network Services":
* A Library (or a mini framework): meaning, closer to the metal, abstracting only what is required for API simplicity, security, error protection and performance. `facil.io` doesn't enforce any external dependencies.
* For Dynamic Protocol: meaning services can change protocols mid-stream. Example use-case would be the HTTP/1.1 upgrade request to Websockets or HTTP/2. HTTP/2 isn't supported just yet, but the current design is easily extendable.
* Network services: meaning implementing client-server or peer2peer network applications, such as web applications, micro-services, etc'.
`facil.h` utilizes `evio.h`, `defer.h` and `sock.h` to create a simple API wrapper around these minimalistic libraries and managing the "glue" that makes them work together for a whole that's greater than the sum of it's parts.
It's simple, it's awesome, and I love it. It's also open-source, MIT licensed and you can test it yourself ;-)
## Core Concepts And A Quick Example
facil.io aims to provide a simple API, so that developers can focus on developing their applications rather then learning new APIs.
However, server applications are not the simplest of beasts, so if you encounter some minor complexity, I hope this documentation can help elevate the situation.
### The Protocol: How Network Services Communicate
By nature, network services implement higher layer protocols to communicate (higher then TCP/IP). The HTTP protocol is a common example.
Typically, server applications "react", they read incoming data (known as a request), perform a task and send output (a response).
The base type to handle the demands of protocols is `protocol_s`. This is no more then a struct with information about the callbacks that should be invoked on network events (incoming data, disconnection etc').
Protocol object instances must be unique per connection, and so they are usually dynamically allocated.
```c
typedef struct {
const char *service; /** A pointer / string to identify the protocol (i.e. "http"). */
void (*on_data)(intptr_t uuid, protocol_s *protocol); /** called when a data is available */
void (*on_ready)(intptr_t uuid, protocol_s *protocol); /** called when the socket is ready to be written to. */
void (*on_shutdown)(intptr_t uuid, protocol_s *protocol); /** called when the server is shutting down */
void (*on_close)(protocol_s *protocol); /** called after the connection was closed */
void (*ping)(intptr_t uuid, protocol_s *protocol); /** called when a connection's timeout was reached */
size_t rsv; /** private metadata used by facil. */
} protocol_s; ///*
```
Protocol object instances should be initiated per connection, since `facil.io` uses a locking mechanism that prevents the same protocol object from running it's callbacks concurrently.
By reviewing the HTTP and Websocket examples in the facil.io codebase, it's easy to see that the `protocol_s` "class" can be easily extended to add more data / features that might be required. "C" objects can use a typecasting style of inheritance which comes very handy when implementing network protocols.
---
### More documentation coming soon.
For now, the comments in the header files should provide great documentation and they can be used with Doxygen (or any documentation creation tool).
# a mini-framework for C web applications
A Web application in C? It's as easy as:
```c
#include "http.h" /* the HTTP facil.io extension */
// We'll use this callback in `http_listen`, to handles HTTP requests
void on_request(http_s *request);
// These will contain pre-allocated values that we will use often
FIOBJ HTTP_HEADER_X_DATA;
// Listen to HTTP requests and start facil.io
int main(int argc, char const **argv) {
// allocating values we use often
HTTP_HEADER_X_DATA = fiobj_str_static("X-Data", 6);
// listen on port 3000 and any available network binding (NULL == 0.0.0.0)
http_listen("3000", NULL, .on_request = on_request, .log = 1);
// start the server
facil_run(.threads = 1);
// deallocating the common values
fiobj_free(HTTP_HEADER_X_DATA);
}
// Easy HTTP handling
void on_request(http_s *request) {
http_set_cookie(request, .name = "my_cookie", .name_len = 9, .value = "data",
.value_len = 4);
http_set_header(request, HTTP_HEADER_CONTENT_TYPE,
http_mimetype_find("txt", 3));
http_set_header(request, HTTP_HEADER_X_DATA, fiobj_str_new("my data", 7));
http_send_body(request, "Hello World!\r\n", 14);
}
```
## facil.io - more than a powerful HTTP/Websockets server library.
[facil.io](http://facil.io) is a C mini-framework for web applications and includes a fast HTTP and Websocket server, a [native Pub/Sub solution](https://facil.io/0.6.x/pubsub/), an optional Redis pub/sub engine, support for custom protocols and some nifty tidbits.
[facil.io](http://facil.io) powers the [HTTP/Websockets Ruby Iodine server](https://github.com/boazsegev/iodine) and it can easily power your application as well.
[facil.io](http://facil.io) provides high performance TCP/IP network services to Linux / BSD (and macOS) by using an evented design that was tested to provide an easy solution to [the C10K problem](http://www.kegel.com/c10k.html).
[facil.io](http://facil.io) prefers a TCP/IP specialized solution over a generic one (although it can be easily adopted for Unix sockets, UDP and other approaches).
[facil.io](http://facil.io) includes a number of libraries that work together for a common goal. Some of the libraries (i.e., some [core types](https://facil.io/0.6.x/types/) the thread-pool library `defer`, the socket library `sock`, the evented IO core `evio` and the [parsers]() can be used independently while others are designed to work together using a modular approach.
I used this library (including the HTTP server) on Linux, Mac OS X and FreeBSD (I had to edit the `makefile` for each environment).
## An easy chatroom example
Here's a simple Websocket chatroom example:
```c
#include "http.h"
#include "pubsub.h"
#include
/* *****************************************************************************
Nicknames
***************************************************************************** */
struct nickname {
size_t len;
char nick[];
};
/* This initalization requires GNU gcc / clang ...
* ... it's a default name for unimaginative visitors.
*/
static struct nickname MISSING_NICKNAME = {.len = 7, .nick = "unknown"};
static FIOBJ CHAT_CHANNEL;
/* *****************************************************************************
Websocket callbacks
***************************************************************************** */
/* We'll subscribe to the channel's chat channel when a new connection opens */
static void on_open_websocket(ws_s *ws) {
/* subscription - easy as pie */
websocket_subscribe(ws, .channel = CHAT_CHANNEL, .force_text = 1);
/* notify everyone about new (named) visitors */
struct nickname *n = websocket_udata(ws);
if (n) {
FIOBJ msg = fiobj_str_new(n->nick, n->len);
fiobj_str_write(msg, " joined the chat.", 17);
pubsub_publish(.channel = CHAT_CHANNEL, .message = msg);
/* cleanup */
fiobj_free(msg);
}
}
/* Free the nickname, if any. */
static void on_close_websocket(ws_s *ws) {
struct nickname *n = websocket_udata(ws);
if (n) {
/* send notification */
FIOBJ msg = fiobj_str_new(n->nick, n->len);
fiobj_str_write(msg, " left the chat.", 15);
pubsub_publish(.channel = CHAT_CHANNEL, .message = msg);
/* cleanup */
fiobj_free(msg);
free(n);
}
}
/* Received a message from a client, format message for chat . */
static void handle_websocket_messages(ws_s *ws, char *data, size_t size,
uint8_t is_text) {
struct nickname *n = websocket_udata(ws);
if (!n)
n = &MISSING_NICKNAME;
/* allocates a dynamic string. knowing the buffer size is faster */
FIOBJ msg = fiobj_str_buf(n->len + 2 + size);
fiobj_str_write(msg, n->nick, n->len);
fiobj_str_write(msg, ": ", 2);
fiobj_str_write(msg, data, size);
if (pubsub_publish(.channel = CHAT_CHANNEL, .message = msg))
fprintf(stderr, "Failed to publish\n");
fiobj_free(msg);
(void)(ws);
(void)(is_text);
}
/* *****************************************************************************
HTTP Handling (Upgrading to Websocket)
***************************************************************************** */
/* Answers simple HTTP requests */
static void answer_http_request(http_s *h) {
http_set_header2(h, (fio_cstr_s){.name = "Server", .len = 6},
(fio_cstr_s){.value = "facil.example", .len = 13});
http_set_header(h, HTTP_HEADER_CONTENT_TYPE, http_mimetype_find("txt", 3));
/* this both sends the response and frees the http handler. */
http_send_body(h, "This is a simple Websocket chatroom example.", 44);
}
/* tests that the target protocol is "websockets" and upgrades the connection */
static void answer_http_upgrade(http_s *h, char *target, size_t len) {
/* test for target protocol name */
if (len != 9 || memcmp(target, "websocket", 9)) {
http_send_error(h, 400);
return;
}
struct nickname *n = NULL;
fio_cstr_s path = fiobj_obj2cstr(h->path);
if (path.len > 1) {
n = malloc(path.len + sizeof(*n));
n->len = path.len - 1;
memcpy(n->nick, path.data + 1, path.len); /* will copy the NUL byte */
}
// Websocket upgrade will use our existing response.
if (http_upgrade2ws(.http = h, .on_open = on_open_websocket,
.on_close = on_close_websocket,
.on_message = handle_websocket_messages, .udata = n))
free(n);
}
/* Our main function, we'll start up the server */
int main(void) {
const char *port = "3000";
const char *public_folder = NULL;
uint32_t threads = 1;
uint32_t workers = 1;
uint8_t print_log = 0;
CHAT_CHANNEL = fiobj_sym_new("chat", 4);
if (http_listen(port, NULL, .on_request = answer_http_request,
.on_upgrade = answer_http_upgrade, .log = print_log,
.public_folder = public_folder) == -1)
perror("Couldn't initiate Websocket service"), exit(1);
facil_run(.threads = threads, .processes = workers);
fiobj_free(CHAT_CHANNEL);
}
```
## Further reading
The code in this project is heavily commented and the header files could (and probably should) be used as the actual documentation.
However, experience shows that a quick reference guide is immensely helpful and that Doxygen documentation is ... well ... less helpful and harder to navigate (I'll leave it at that for now).
The documentation in this folder includes:
* A [Getting Started Guide](getting_started.md) with example for WebApps utilizing the HTTP / Websocket protocols as well as a custom protocol.
* A [quick guide to `facil.io`'s dynamic type library](https://facil.io/0.6.x/fiobj/) with quick examples get you started.
* The core [`facil.io` API documentation](https://facil.io/0.6.x/facil/).
This documents the main library API and should be used when writing custom protocols. This API is (mostly) redundant when using the `http` or `websockets` protocol extensions.
* The [`http` extension API documentation]() (Please help me write this).
The `http` protocol extension allows quick access to the HTTP protocol necessary for writing web applications.
Although the `libserver` API is still accessible, the `http_request_s` and `http_response_s` objects and API provide abstractions for the higher level HTTP protocol and should be preferred.
* The [`websockets` extension API documentation]() (Please help me write this).
The `websockets` protocol extension allows quick access to the HTTP and Websockets protocols necessary for writing real-time web applications.
Although the `libserver` API is still accessible, the `http_request_s`, `http_response_s` and `ws_s` objects and API provide abstractions for the higher level HTTP and Websocket protocols and should be preferred.
* Core documentation that documents the libraries used internally.
The core documentation can be safely ignored by anyone using the `facil.io`, `http` or `websockets` frameworks.
The core libraries include (coming soon):
* [`defer`](https://facil.io/0.6.x/defer/) - A simple event-loop with the added functionality of a thread pool and `fork`ing support.
* [`sock`](https://facil.io/0.6.x/sock/) - A sockets library that resolves common issues such as fd collisions and user land buffer.
* [`evio`](https://facil.io/0.6.x/evio/) - an edge triggered `kqueue` / `epoll` abstraction / wrapper with an overridable callback design, allowing fast access to these APIs while maintaining portability enhancing ease of use (at the expense of complexity).
---
## Forking, Contributing and all that Jazz
Sure, why not. If you can add Solaris or Windows support to `evio`, that could mean `facil.io` would become available for use on these platforms as well (as well as the HTTP protocol implementation and all the niceties that implies).
If you encounter any issues, open an issue (or, even better, a pull request with a fix) - that would be great :-)
Hit me up if you want to:
* Help me write HPACK / HTTP2 protocol support.
* Help me design / write a generic HTTP routing helper library for the `http_s` struct.
* If you want to help integrate an SSL/TLS library into `facil`, that would be great.
# A Simple Socket Library for non-blocking Sockets
This library aims to:
* Resolve the file descriptor collision security risk.
* Resolve issues with partial writes and concurrent write operations by implementing a user land buffer and a spinlock.
* Provide `sendfile` alternatives when sending big data stored on the disk - `sendfile` being broken on some system and lacking support for TLS.
* Provide a solution to the fact that closing a connection locally (using `close`) will prevent event loops and socket polling systems (`epoll`/`kqueue`) from raising the `on_hup` / `on_close` event. `libsock` provides support for local closure notification - this is done by defining an optional `reactor_on_close(intptr_t uuid)` overridable function.
* Provide support for timeout a management callback for server architectures that require the timeout to be reset ("touched") whenever a read/write occurs - this is done by defining an optional `sock_touch(intptr_t uuid)` callback.
`libsock` requires only the following three files from this repository: [`src/spnlock.h`](../src/spnlock.h), [`src/libsock.h`](../src/libsock.h) and [`src/libsock.c`](../src/libsock.c).
#### The Story Of The Partial `write`
For some unknown reason, there is this little tidbit of information for the `write` function family that is always written in the documentation and is commonly ignored by first time network programmers:
> On success, **the number of bytes written** is returned (zero indicates nothing was written). On error, -1 is returned, and errno is set appropriately.
It is often missed that the number of bytes actually written might be smaller then the amount we wanted to write.
For this reason, `libsock` implements a user land buffer. All calls to `sock_write` promise to write the whole of the data (careful) unless a connection issue causes them to fail.
The only caveat is for very large amounts of data that exceed the available user land buffer (which you can change from the default ~16Mib), since `libsock` will keep flushing all the sockets (it won't return from the `sock_write` function call) until the data can be fully written to the user-land buffer.
However, this isn't normally an issue, since files can be sent at practically no cost when using `libsock` and large amounts of data are normally handled by files (temporary or otherwise).
#### The `sendfile` Experience
The `sendfile` directive is cool, it allows us to send data directly from a file to a socket, no copying required (although the kernel might or might not copy data).
However, the `sendfile` function call is useless when working with TLS connections, as the TLS implementation is performed in the application layer, meaning that the data needs to be encrypted in the application layer... So, no `sendfile` for TLS connections :-(
Another issue is that [`sendfile` is broken / unavailable on some systems](https://blog.phusion.nl/2015/06/04/the-brokenness-of-the-sendfile-system-call/)...
`libsock` Provides a dual solution for this:
1. On systems where `sendfile` is available, it will be used if no TLS or other read/write hook had been defined.
2. Where `sendfile` can't be used, `libsock` will write the file to the socket (or TLS), loading up to ~16Kb of data at a time.
The same file (fd) can be sent to multiple clients at once (keeping it open in the application's cache) or automatically closed once it was sent.
This abstraction of the `sendfile` system call elevates the headache related with managing resources when sending big files.
#### Postponing The Timeout
Often, server applications need to implement a timeout review procedure that checks for stale connections.
However, this requires that the server architecture to implement a "write" and "read" logic (if only to set or reset the timeout for the connection) and somehow enforce this API, so that timeout events don't fire prematurely.
`libsock` saves us this extra work by providing an optional callback that allows server architectures to update their timeout state without implementing the whole "read/write" API stack.
By providing a `sock_touch` function that can be overwritten (a weak symbol function), the server architecture need only implement the `sock_touch` function to update any internal data structure or linked list, and `libsock` takes care of the rest.
This means that on any successful `sock_read`, `sock_write` or `sock_flush`, where data was written to the socket layer, `sock_touch` is called.
#### The Lost `on_close` Event
When polling using `kqueue` / `epoll` / the evented library of your choice, it is normal for locally closed sockets (when we call `close`) to close quietly, with no event being raised.
This can be annoying at times and often means that a wrapper is supplied for the `close` function and hopes are that no one calls `close` directly.
However, whenever a connection is closed using `libsock`, a callback `reactor_on_close(intptr_t)` is called.
This is supported by two facts: 1. because `sock_close` acts as a wrapper, calling the callback after closing the connection; 2. because connections are identified using a UUID (not an `fd`), making calls to `close` harder (though possible).
`sock_close` will always call the `reactor_on_close` callback, where you can call the function of your choice and presto: a local `close` will evoke the `on_close` event callback for the evented library of your choice.
The `reactor_on_close` name was chosen to have `libsock` work with `libreact`. This is a convenience, not a requirement.
It should be noted that a default `reactor_on_close` is provided, so there's no need to write one if you don't need one.
#### UUIDs & The File Descriptor Collision Security Risk
These things happen, whether we're using threads or evented programming techniques, the more optimized our code the more likely that we are exposed to file description collision risks.
Here is a quick example:
* Bob connects to his bank to get a bank statement on line.
* Bob receives the file descriptor 12 for the new connection and submits a request to the server.
* A request to the bank's database is performed in a non-blocking manner (evented, threaded, whatever you fancy). But, due to system stress or design or complexity, it will take a longer time to execute.
* Bob's connection is dropped for some reason and file descriptor 12 is released by the system.
* Alice connects to the server and receives the (now available) file descriptor 12 for the new connection (Alice can even negotiate a valid TLS connection at this point).
* The database response arrives and the information is sent to the file descriptor.
* Alice gets Bob's bank statement.
... Hmmm... bad.
The risk might seem unlikely, but it exists. These possible collisions in read/write operations are one of the main reasons why operating systems hold on to a closed socket's file descriptor for a bit longer than they need (often a minute or two).
Hence: `sock.h` uses connection UUIDs to map to the underlying file descriptors.
These UUIDs are the C standard `intptr_t` type, which means that they are easy to handle, store and move around. The UUIDs are a simple system local scheme and shouldn't be shared among systems or processes (collision risks). The only certainly invalid UUID is `-1` (depending on the system's byte-ordering scheme).
In our example:
* Bob connects to his bank to get a bank statement on line.
* Bob receives the file descriptor 12, mapped to the connection UUID 0x114.
* [...] everything the same until Bob's connection drops.
* Alice connects to the server and receives the (now available) file descriptor 12 for the new connection. The connection is mapped to the UUID 0x884.
* The database response arrives but isn't sent because the write operation fails (invalid UUID 0x114).
So, using `sock.h`, Alice will **not** receive Bob's bank statement.
#### TCP/IP and Unix Sockets as default
`sock.h` defaults to TCP/IP and Unix Domain sockets, so calls to `sock_listen` and `sock_connect` will assume one or the other (if no port is assigned, it is assumed to be a Unix Domain socket).
However, importing other socket types (such as pipes) should be possible by using the `sock_open(fd)` function, that imports an existing file descriptor into `sock.h`, assigning this file descriptor a valid UUID.
# The FIOBJ Data Stream Type
### Creating the Data Stream object
#### `fiobj_data_newstr`
```c
FIOBJ fiobj_data_newstr(void);
```
Creates a new local in-memory Data Stream object.
#### `fiobj_data_newstr2`
```c
FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
void (*dealloc)(void *));
```
Creates a Data object from an existing buffer.
The buffer will be deallocated using the provided `dealloc` function pointer.
Use a NULL `dealloc` function pointer if the buffer is static and shouldn't be freed.
#### `fiobj_data_newtmpfile`
```c
FIOBJ fiobj_data_newtmpfile(void);
```
Creates a new local tempfile Data Stream object.
#### `fiobj_data_newfd`
```c
FIOBJ fiobj_data_newfd(int fd);
```
Creates a new local file Data Stream object.
#### `fiobj_data_slice`
```c
FIOBJ fiobj_data_slice(FIOBJ parent, intptr_t offset, uintptr_t length);
```
Creates a slice from an existing Data Stream object.
### Saving the Data Stream object
#### `fiobj_data_save`
```c
int fiobj_data_save(FIOBJ io, const char *filename);
```
Creates a new local file Data Stream object.
### Reading Data From The Stream
#### `fiobj_data_read`
```c
fio_cstr_s fiobj_data_read(FIOBJ io, intptr_t length);
```
Reads up to `length` bytes and returns a temporary(!) buffer object (not NUL terminated).
If `length` is zero or negative, it will be computed from the end of the input backwards (0 == EOF).
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### `fiobj_data_read2ch`
```c
fio_cstr_s fiobj_data_read2ch(FIOBJ io, uint8_t token);
```
Reads until the `token` byte is encountered or until the end of the stream.
Returns a temporary(!) C string including the end of line marker.
Careful when using this call on large file streams, as the whole file stream might be loaded into the memory.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### ``
```c
#define fiobj_data_gets(io) fiobj_data_read2ch((io), '\n');
```
Reads a line (until the '\n' byte is encountered) or until the end of the available data.
Returns a temporary(!) buffer object (not NUL terminated) including the end of line marker.
Careful when using this call on large file streams, as the whole file stream might be loaded into the memory.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
#### `fiobj_data_pos`
```c
intptr_t fiobj_data_pos(FIOBJ io);
```
Returns the current reading position. Returns -1 on error.
#### `fiobj_data_len`
```c
intptr_t fiobj_data_len(FIOBJ io);
```
Returns the length of the stream.
#### `fiobj_data_seek`
```c
void fiobj_data_seek(FIOBJ io, intptr_t position);
```
Moves the reading position to the requested position.
#### `fiobj_data_pread`
```c
fio_cstr_s fiobj_data_pread(FIOBJ io, intptr_t start_at, uintptr_t length);
```
Reads up to `length` bytes starting at `start_at` position and returns a temporary(!) buffer object (not NUL terminated) string object. The reading position is ignored and unchanged.
The C string object will be invalidate the next time a function call to the Data Stream object is made.
### Writing To The Data Stream
#### `fiobj_data_write`
```c
intptr_t fiobj_data_write(FIOBJ io, void *buffer, uintptr_t length);
```
Writes `length` bytes at the end of the Data Stream stream, ignoring the reading position.
Behaves and returns the same value as the system call `write`.
#### `fiobj_data_puts`
```c
intptr_t fiobj_data_puts(FIOBJ io, void *buffer, uintptr_t length);
```
Writes `length` bytes at the end of the Data Stream stream, ignoring the reading position, adding an EOL marker ("\r\n") to the end of the stream.
Behaves and returns the same value as the system call `write`.
#### `fiobj_data_assert_dynamic`
```c
void fiobj_data_assert_dynamic(FIOBJ io);
```
Makes sure the Data Stream object isn't attached to a static or external string.
If the Data Stream object is attached to a static or external string, the data will be copied to a new memory block.
If the Data Stream object is a slice from another Data Stream object, the data will be copied and the type of Data Stream object (memory vs. tmpfile) will be inherited.
# Core types in facil.io
At it's core, facil.io utilizes a number of core types that make it easier to develop network applications.
These types are divided into three categories:
1. Core types: [dynamic arrays](https://facil.io/0.6.x/fio_ary/), [hash maps](https://facil.io/0.6.x/fio_hashmap/) and [linked lists](https://facil.io/0.6.x/fio_llist/).
1. Core Object types: the [facil.io object types (`FIOBJ`)](https://facil.io/0.6.x/fiobj/).
1. Network / API specific types: these are specific types that are used in specific function calls or situations, such as `protocol_s`, `http_s` etc'.
These types will be documented along with their specific API / extension in the [Modules](https://facil.io/0.6.x/modules/) section.
Here I will provide an overview for the first two categories, core types and objects.
## Core types overview
Many tasks in C could require a Linked List, a Dynamic Array or a Hashmap.
This is why facil.io includes these three core types as single file libraries that use macros and inline-functions.
These core types are fairly easy to use and the single file libraries are well documented.
### Memory Ownership
Note that unlike the FIOBJ object system, facil.io's core types don't have prior information about the memory model of the objects they store.
For this reason, although objects are stored within a `fio_ls_s`, `fio_ary_s` or `fio_hash_s`, they should be freed manually once they are ejected from their container.
On the other hand, FIOBJ Arrays and Hashmaps know the FIOBJ memory model and can (and will) free any objects they hold when appropriate.
### Type Memory Management
All the core types are divided into two parts:
1. A container which must be initialized (and perhaps destroyed).
1. Internal data that must be allocated and deallocated.
The container could be placed on the stack as well as allocated on the heap.
For example, here's an array with a container placed on the stack:
```c
#include "fio_ary.h"
int main(void) {
// The array container.
fio_ary_s ary;
// allocate and initialize internal data
fio_ary_new(&ary, 0);
// perform some actions
fio_ary_push(&ary, (void *)1);
FIO_ARY_FOR(&ary, pos) {
printf("index: %lu == %lu\n", (unsigned long)pos.i, (unsigned long)pos.obj);
}
// free the internal data to avoid memory leaks
fio_ary_free(&ary);
}
```
Here's the same example, with the array container dynamically allocated:
```c
#include "fio_ary.h"
int main(void) {
// The array container.
fio_ary_s *ary = malloc(sizeof(*ary));
if (!ary)
exit(-1);
// allocate and initialize internal data
fio_ary_new(ary, 0);
// perform some actions
fio_ary_push(ary, (void *)1);
FIO_ARY_FOR(ary, pos) {
printf("index: %lu == %lu\n", (unsigned long)pos.i, (unsigned long)pos.obj);
}
// free the internal data to avoid memory leaks
fio_ary_free(ary);
free(ary);
}
```
### Core Types API
The API for the core types is documented within the source files. To read more about each type click the links here:
1. [Dynamic Arrays](https://facil.io/0.6.x/fio_ary/)
1. [HashMaps](https://facil.io/0.6.x/fio_hashmap/)
1. [Linked Lists](https://facil.io/0.6.x/fio_llist/).