LIBFYAML-MISC(3)
okay so bestie, you found LIBFYAML-MISC(3) β the libfyaml-misc page from the libfyaml manual. Buckle up, because this one is the low-level toolkit drawer: tiny macros, portability shims, and preprocessor sorcery. π
β¨
πΈ NAME πΈ
libfyaml-misc β libfyaml miscellaneous API π
π§° UTILITIES π§°
Okay so this header is literally the low-level building-blocks bin shared by every other public libfyaml header. It has no libfyaml dependencies of its own and is safe to include in isolation. No cap. π«Ά
π Platform portability
ssize_tshim for MSVC / Windows toolchains π»struct iovecshim for Windows (sys/uio.hsubstitute) πͺFY_EXPORT/FY_DEPRECATED/FY_FORMATβ symbol visibility and compiler-attribute portability macros β¨FY_UNUSED,FY_ALWAYS_INLINE,FY_DEBUG_UNUSEDβ compiler hint wrappers with safe fallbacks on non-GCC/Clang toolchains πFY_CONSTRUCTOR/FY_DESTRUCTORβ GCC-style constructor/destructor attributes with availability guards (FY_HAS_CONSTRUCTOR,FY_HAS_DESTRUCTOR) ποΈ
π’ Core constants and sentinel values
FY_BIT(x)β single-bit mask (1U << x) π―FY_NTβ sentinel meaning βnull-terminatedβ; pass as a length argument to indicate that the string length should be inferred viastrlenπ
π§ Memory helpers
FY_ALLOCA_COPY_FREE/FY_ALLOCA_COPY_FREE_NO_NULLβ copy amalloc-returned string onto the stack and immediately free the heap allocation; produces an alloca-lifetime copy in a single expression π§fy_vsprintfa/fy_sprintfaβ printf-style formatting into a stack-allocated (alloca) buffer; the result is valid for the lifetime of the enclosing function π¨οΈ
π¦ Container and array helpers
container_ofβ recover a pointer to an enclosing struct from a pointer to one of its members π¦ARRAY_SIZEβ number of elements in a stack-allocated array π’
𧬠Type safety and compile-time checks
FY_SAME_TYPE/FY_CHECK_SAME_TYPEβ assert two expressions share the same C type (wraps__builtin_types_compatible_pwith a fallback) π§¬FY_COMPILE_ERROR_ON_ZEROβ produce a compile-time error when the argument evaluates to zero; used for macro-level static assertions π¨
β Overflow-safe arithmetic
FY_ADD_OVERFLOW,FY_SUB_OVERFLOW,FY_MUL_OVERFLOWβ wrappers around__builtin_*_overflowwith portable fallback implementations βββοΈ
π Miscellaneous
FY_IMPOSSIBLE_ABORTβ mark unreachable code paths; aborts at runtime πFY_STACK_SAVE/FY_STACK_RESTOREβ save and restore the stack pointer for alloca-based temporary arenas π₯- Lambda / closure macros and a CPP metaprogramming framework for building variadic generic APIs π§
- Diagnostic helpers and floating-point precision constants π¬
This section implements a portable FY_CPP_* macro toolkit that enables applying an operation to every argument in a __VA_ARGS__ list β the equivalent of a compile-time map() function. Obsessed. πΊοΈ
The core challenge: The C preprocessor does not support recursion. A macro cannot call itself. The standard workaround is deferred evaluation: a macro is βpostponedβ so that it is not expanded in the current scan pass, only in a future one. Multiple passes are forced by the FY_CPP_EVALn ladder which re-expands the token stream a power-of-two number of times.
πͺ Evaluation levels (FY_CPP_EVAL1 .. FY_CPP_EVAL)
FY_CPP_EVAL1β one pass (no re-expansion)FY_CPP_EVAL2β 2 passesFY_CPP_EVAL4β 4 passesFY_CPP_EVAL8β 8 passesFY_CPP_EVAL16β 16 passes (GCC only; Clang craps out at 8) πFY_CPP_EVALβ full depth (32 passes on GCC, 16 on Clang)
Each doubling allows mapping over twice as many arguments. FY_CPP_EVAL supports lists of up to ~32 elements (GCC) or ~16 elements (Clang) in practice.
π― Argument accessors
Extract positional arguments from __VA_ARGS__:
FY_CPP_FIRST(...)β first argument (0if list is empty)FY_CPP_SECOND(...)β second argumentFY_CPP_THIRD(...)β third argumentFY_CPP_FOURTH(...)β fourth argumentFY_CPP_FIFTH(...)β fifth argumentFY_CPP_SIXTH(...)β sixth argumentFY_CPP_REST(...)β all arguments after the first (empty if < 2)
πΊοΈ Map operations
FY_CPP_MAP(macro, ...)β expandmacro(x)for each argumentx.FY_CPP_MAP2(a, macro, ...)β expandmacro(a, x)for eachx, threading a fixed first argumentathrough every call.
π§° Utility
FY_CPP_VA_COUNT(...)β number of arguments (integer expression).FY_CPP_VA_ITEMS(_type, ...)β compound-literal array of_typefrom varargs.FY_CPP_EMPTY()β deferred empty token (used to postpone macros).FY_CPP_POSTPONE1(macro)β postpone a one-argument macro one pass.FY_CPP_POSTPONE2(a, macro)β postpone a two-argument macro one pass.
π·οΈ Short-name mode (FY_CPP_SHORT_NAMES)
By default the implementation uses abbreviated internal names (_E1, _FM, etc.) to reduce token-expansion buffer pressure. Define FY_CPP_SHORT_NAMES_DEBUG before including this header to force the original long-name implementation (FY_CPP_EVAL1, _FY_CPP_MAP_ONE, etc.) which is easier to read in expansion traces.
Example:
// Count and collect variadic integer arguments into a C array:
int do_sum(int count, int *items) { ... }
#define do_sum_macro(...) \
do_sum(FY_CPP_VA_COUNT(__VA_ARGS__), \
FY_CPP_VA_ITEMS(int, __VA_ARGS__))
// do_sum_macro(1, 2, 5, 100) expands to:
// do_sum(4, ((int [4]){ 1, 2, 5, 100 }))
Portable wrappers around GCC/Clang #pragma GCC diagnostic directives. Use FY_DIAG_PUSH() and FY_DIAG_POP() to bracket a region where a specific warning is suppressed:
FY_DIAG_PUSH
FY_DIAG_IGNORE_ARRAY_BOUNDS
... code that triggers -Warray-bounds ...
FY_DIAG_POP
On compilers other than GCC/Clang all macros in this group expand to nothing.
Portable wrappers for the mantissa-digit and decimal-digit counts of float, double, and long double. Each macro tries three sources in order:
- The standard
<float.h>macro (e.g.FLT_MANT_DIG). - The GCC/Clang predefined macro (e.g.
__FLT_MANT_DIG__). - A conservative hard-coded fallback.
These constants are used when formatting floating-point values as YAML scalars to ensure round-trip fidelity. π
π§ macro __has_builtin
__has_builtin(x)
Parameters
- x β the builtin name to query.
Description
Always evaluates to 0 (false) so that all #if __has_builtin(...) guards safely fall back to the portable implementation.
Return
0.
π― macro FY_BIT
FY_BIT(x)
Produce an unsigned bitmask with bit x set.
Parameters
- x β zero-based bit position (0β31 for a 32-bit result).
Return
1U << x.
π macro FY_NT
FY_NT()
Sentinel value meaning βnull-terminated; compute length at runtimeβ.
Description
Pass as the len argument to any libfyaml function that accepts a (const char *str, size_t len) pair to indicate that str is a NUL-terminated C string whose length should be determined with strlen().
Value: (size_t)-1
β¨ macro FY_EXPORT
FY_EXPORT()
Mark a symbol as part of the shared-library public ABI.
Description
On GCC/Clang (version >= 4) expands to __attribute__((visibility("default"))), overriding -fvisibility=hidden for the annotated symbol. On other compilers expands to nothing (all symbols are visible by default).
π macro FY_DEPRECATED
FY_DEPRECATED()
Mark a function or variable as deprecated.
Description
On GCC/Clang expands to __attribute__((deprecated)), causing a compile-time warning whenever the annotated symbol is used. On other compilers expands to nothing.
π§Ύ macro FY_FORMAT
FY_FORMAT(_t, _x, _y)
Annotate a function with printf-style format checking.
Parameters
- _t β format type token (e.g.
printf,scanf,strftime). - _x β 1-based index of the format-string parameter.
- _y β 1-based index of the first variadic argument (
0forva_listwrappers).
Description
On GCC/Clang expands to __attribute__((format(_t, _x, _y))), enabling the compiler to type-check the format string and variadic arguments.
π§ macro FY_ALLOCA_COPY_FREE
FY_ALLOCA_COPY_FREE(_str, _len)
Copy a heap string onto the stack and free the original.
Parameters
- _str β heap-allocated string to copy and free (may be
NULL). - _len β length in bytes, or
FY_NTto usestrlen().
Expands to a statement expression that
- Copies
_str(up to_lenbytes, or the full NUL-terminated length when_lenisFY_NT) into a NUL-terminated stack buffer viaalloca(). - Calls
free(@_str)to release the heap allocation. - Evaluates to a
const char *pointing to the stack copy.
When _str is NULL the macro evaluates to NULL and performs no copy or free.
The stack buffer is valid only for the lifetime of the enclosing function. Do not call this in a loop β each invocation grows the stack frame. π«
Return
const char * to the stack copy, or NULL if _str was NULL.
π§ macro FY_ALLOCA_COPY_FREE_NO_NULL
FY_ALLOCA_COPY_FREE_NO_NULL(_str, _len)
Like FY_ALLOCA_COPY_FREE() but returns "" for NULL.
Parameters
- _str β heap-allocated string to copy and free (may be
NULL). - _len β length in bytes, or
FY_NTto usestrlen().
Description
Identical to FY_ALLOCA_COPY_FREE but substitutes an empty string literal "" when _str is NULL, so callers never receive a NULL pointer.
Return
const char * to the stack copy, or "" if _str was NULL.
π¦ macro container_of
container_of(ptr, type, member)
Recover a pointer to a containing struct from a member pointer.
Parameters
- ptr β pointer to the member field.
- type β type of the containing struct.
- member β name of the member field within
type.
Description
Given a pointer ptr to a field named member inside a struct of type type, returns a pointer to the enclosing type instance.
Uses __typeof__ to catch type mismatches at compile time (GCC/Clang).
Return
Pointer to the containing struct of type type.
π’ macro ARRAY_SIZE
ARRAY_SIZE(x)
Compute the number of elements in a stack-allocated array.
Parameters
- x β the array expression.
Description
Evaluates to a compile-time constant. Only valid for arrays with a known size at compile time (not pointers or VLAs).
Return
Number of elements, as a size_t.
π macro FY_UNUSED
FY_UNUSED()
Suppress βunused variable/parameterβ warnings.
On GCC/Clang (version >= 4) it expands to __attribute__((unused)). On every other compiler it just expands to nothing, no drama.
Use it on parameters or local variables that you're intentionally not referencing β like, the classic case is debug-only code paths where the variable exists but only gets touched inside your debug branches. π
π macro FY_CONSTRUCTOR
FY_CONSTRUCTOR()
Run a function automatically before main().
Description β On GCC/Clang this expands to __attribute__((constructor)). It also defines FY_HAS_CONSTRUCTOR so callers can detect support at compile time. On other compilers it expands to nothing and FY_HAS_CONSTRUCTOR is not defined. So like, check for the macro before you get your hopes up. β¨
π macro FY_DESTRUCTOR
FY_DESTRUCTOR()
Run a function automatically after main() (or on exit()).
Description β On GCC/Clang this expands to __attribute__((destructor)). It also defines FY_HAS_DESTRUCTOR so callers can detect support at compile time. On other compilers it expands to nothing and FY_HAS_DESTRUCTOR is not defined.
π macro FY_DEBUG_UNUSED
FY_DEBUG_UNUSED()
Mark a variable as potentially unused in non-debug builds.
Description β Expands to __attribute__((unused)) on GCC/Clang when NDEBUG is defined, which silences the warnings for variables that are only referenced inside assert() calls (those literally vanish in release builds, so the variable looks abandoned π). In debug builds (NDEBUG not set) it expands to nothing.
π macro FY_IMPOSSIBLE_ABORT
FY_IMPOSSIBLE_ABORT(void)
Assert that an unreachable code path has been reached.
Parameters
- void β no arguments
Description β Calls assert(0) followed by abort() to terminate the process immediately. Use it to flag code paths that must never execute in a correct program β the textbook case being the default branch of a switch that already covers every enum value. π
The double invocation is the whole point: it guarantees termination even when assertions are disabled (NDEBUG). No sneaking past this one, bestie.
π macro FY_COMPILE_ERROR_ON_ZERO
FY_COMPILE_ERROR_ON_ZERO(_e)
Trigger a compile error if expression _e is zero.
Parameters
- _e β Compile-time expression that must be non-zero.
Description β Evaluates _e at compile time. If _e is zero, sizeof(char[-1]) is ill-formed and the build just... fails. π If _e is non-zero, the whole thing is a no-op void cast.
PreferFY_CHECK_SAME_TYPEorstatic_assertif you want clearer error messages β use this primitive when you actually need a compile-time boolean inside a macro context.
π macro FY_SAME_TYPE
FY_SAME_TYPE(_a, _b)
Test whether two expressions have the same type.
Parameters
- _a β First expression.
- _b β Second expression.
Description β On GCC/Clang it uses __builtin_types_compatible_p to compare the types of _a and _b (via __typeof__). On compilers that don't have that builtin it always evaluates to true, specifically to avoid false negatives. (Iconic defensive behaviour, honestly.)
Return β Non-zero (true) if the types match, zero (false) otherwise.
π macro FY_CHECK_SAME_TYPE
FY_CHECK_SAME_TYPE(_a, _b)
Trigger a compile error if two expressions have different types.
Parameters
- _a β First expression.
- _b β Second expression.
Description β Combines FY_SAME_TYPE and FY_COMPILE_ERROR_ON_ZERO. Use it in macros that require two operands to have the same type, e.g. overflow-safe arithmetic. π«Ά
π macro FY_ADD_OVERFLOW
FY_ADD_OVERFLOW(_a, _b, _resp)
Checked addition: detect signed/unsigned integer overflow.
Parameters
- _a β First operand.
- _b β Second operand (must have the same type as
_a). - _resp β Pointer to receive the result (written even on overflow).
Description β On GCC/Clang it maps directly to __builtin_add_overflow(_a, _b, _resp), which is a single compiler intrinsic with full type generality. Gorgeous. β¨
On other compilers a portable fallback is used that
- Requires
_aand_bto have the same type (enforced at compile time viaFY_CHECK_SAME_TYPE). - Computes
*_resp = _a + _band returns true if the addition wrapped.
Return β true if the addition overflowed, false otherwise.
π macro FY_SUB_OVERFLOW
FY_SUB_OVERFLOW(_a, _b, _resp)
Checked subtraction: detect signed/unsigned integer overflow.
Parameters
- _a β Minuend.
- _b β Subtrahend (must have the same type as
_a). - _resp β Pointer to receive the result (written even on overflow).
Description β On GCC/Clang it maps to __builtin_sub_overflow(_a, _b, _resp). The portable fallback requires _a and _b to have the same type.
Return β true if the subtraction overflowed, false otherwise.
π macro FY_MUL_OVERFLOW
FY_MUL_OVERFLOW(_a, _b, _resp)
Checked multiplication: detect signed/unsigned integer overflow.
Parameters
- _a β First factor.
- _b β Second factor (must have the same type as
_a). - _resp β Pointer to receive the result (written even on overflow).
Description β On GCC/Clang it maps to __builtin_mul_overflow(_a, _b, _resp). The portable fallback requires _a and _b to have the same type, and detects overflow by dividing the product back and comparing it with the original operand. Sneaky but it works. π
Return β true if the multiplication overflowed, false otherwise.
π macro fy_sprintfa
fy_sprintfa(_fmt, ...)
Format a string into a stack buffer using inline arguments.
Parameters
- _fmt β
printf-style format string. - ellipsis β Format arguments.
Description β Like fy_vsprintfa() but it takes the arguments directly (uses __VA_ARGS__). Two calls to snprintf() happen under the hood: the first with a NULL buffer to measure the required length, the second to actually write the result into a stack-allocated buffer.
Heads up bestie: the returned pointer is only valid for the lifetime of the enclosing function. Do not let it escape. π
Return β char * to a NUL-terminated stack buffer.
π macro FY_CPP_EVAL1
FY_CPP_EVAL1(...)
Force one additional macro-expansion pass.
Parameters
- ellipsis β Token sequence to re-expand.
Description β Expands its argument list once. Use it as a building block for the deeper evaluation levels; for most uses just reach for FY_CPP_EVAL() instead. π§
π macro FY_CPP_EVAL2
FY_CPP_EVAL2(...)
Force two additional macro-expansion passes.
Parameters
- ellipsis β Token sequence to re-expand.
π macro FY_CPP_EVAL4
FY_CPP_EVAL4(...)
Force four additional macro-expansion passes.
Parameters
- ellipsis β Token sequence to re-expand.
π macro FY_CPP_EVAL8
FY_CPP_EVAL8(...)
Force eight additional macro-expansion passes.
Parameters
- ellipsis β Token sequence to re-expand.
π macro FY_CPP_EVAL16
FY_CPP_EVAL16(...)
Force 16 additional macro-expansion passes (GCC only).
Parameters
- ellipsis β Token sequence to re-expand.
Description β Not defined on Clang, because Clang exhausts its expansion buffer before it ever reaches 16 levels. π Instead, wrap FY_CPP_MAP() / FY_CPP_MAP2() in FY_CPP_EVAL() rather than calling this one directly.
π macro FY_CPP_EVAL
FY_CPP_EVAL(...)
Force maximum macro-expansion depth.
Parameters
- ellipsis β Token sequence to fully expand.
Description β On GCC it performs 32 expansion passes (so it supports lists up to ~32 elements). On Clang it performs 16 passes (so lists up to ~16 elements). π
Always use this one rather than a specific EVALn β unless you genuinely have a known bound on the number of arguments.
π macro FY_CPP_EMPTY
FY_CPP_EMPTY(void)
Produce an empty token sequence (deferred).
Parameters
- void β no arguments
Description β Expands to nothing. It's used together with FY_CPP_POSTPONE1() / FY_CPP_POSTPONE2() to defer macro expansion by one scan pass, which breaks the preprocessor's blue-paint recursion guard. Yes, that's the actual name. Yes, it's giving warlock. π§ββοΈπ
π macro FY_CPP_POSTPONE1
FY_CPP_POSTPONE1(macro)
Defer a single-argument macro by one expansion pass.
Parameters
- macro β Macro token to postpone.
Description β Expands to the macro name macro followed by a deferred FY_CPP_EMPTY(), so the macro call doesn't complete until the next pass. Delayed gratification, preprocessor edition. β³
π macro FY_CPP_POSTPONE2
FY_CPP_POSTPONE2(a, macro)
Defer a two-argument macro by one expansion pass.
Parameters
- a β Fixed first argument (carried along, not expanded yet).
- macro β Macro token to postpone.
Description β Like FY_CPP_POSTPONE1() but for macros that take a leading fixed argument a.
π macro FY_CPP_FIRST
FY_CPP_FIRST(...)
Extract the first argument from a variadic list.
Parameters
- ellipsis β Variadic argument list.
Description β Returns the first argument, or 0 if the list is empty.
Return β First argument token, or 0.
π macro FY_CPP_SECOND
FY_CPP_SECOND(...)
Extract the second argument from a variadic list.
Parameters
- ellipsis β Variadic argument list.
Description β Returns the second argument, or 0 if fewer than two arguments are present.
Return β Second argument token, or 0.
π macro FY_CPP_THIRD
FY_CPP_THIRD(...)
Extract the third argument.
Parameters
- ellipsis β Variadic argument list. Returns: Third argument, or
0.
π macro FY_CPP_FOURTH
FY_CPP_FOURTH(...)
Extract the fourth argument.
Parameters
- ellipsis β Variadic argument list. Returns: Fourth argument, or
0.
π macro FY_CPP_FIFTH
FY_CPP_FIFTH(...)
Extract the fifth argument.
Parameters
- ellipsis β Variadic argument list. Returns: Fifth argument, or
0.
π macro FY_CPP_SIXTH
FY_CPP_SIXTH(...)
Extract the sixth argument.
Parameters
- ellipsis β Variadic argument list. Returns: Sixth argument, or
0.
π macro FY_CPP_REST
FY_CPP_REST(...)
Return all arguments after the first.
Parameters
- ellipsis β Variadic argument list.
Description β Expands to the tail of the variadic list with the first element removed. Expands to nothing if the list has zero or one element. π
π macro FY_CPP_MAP
FY_CPP_MAP(macro, ...)
Apply a macro to every argument in a variadic list.
Parameters
- macro β Single-argument macro to apply.
- ellipsis β Arguments to map over.
β¨ Description β¨
okay so this one expands macro(x) for each argument x in __VA_ARGS__, concatenating all the results. The argument list must be non-empty β no empty vibes allowed, bestie. It uses FY_CPP_EVAL() internally, so the list length is bounded by the evaluation depth.
For example:
#define PRINT_ITEM(x) printf("%d\n", x);
FY_CPP_MAP(PRINT_ITEM, 1, 2, 3)
// expands to: printf("%d\n", 1); printf("%d\n", 2); printf("%d\n", 3);
π¦ macro FY_CPP_VA_COUNT π¦
FY_CPP_VA_COUNT(...)
Count the number of arguments in a variadic list β literally just counts them for you.
π Parameters π
- ellipsis (
ellipsis) β Variadic argument list.
π Description π
Evaluates to a compile-time integer expression equal to the number of arguments. Returns 0 for an empty list.
π Return π
Integer expression giving the argument count.
πΈ macro FY_CPP_VA_ITEMS πΈ
FY_CPP_VA_ITEMS(_type, ...)
Build a compound-literal array from variadic arguments. it's giving "turn my varargs into an array" energy.
π Parameters π
_typeβ Element type of the resulting array.- ellipsis (
ellipsis) β Values to place in the array.
π Description π
Expands to a compound literal of type _type[N] (where N is the number of arguments) initialised with the provided values. Useful for passing a variadic argument list as an array to a function.
For example:
// FY_CPP_VA_ITEMS(int, 1, 2, 5, 100)
// expands to: ((int [4]){ 1, 2, 5, 100 })
π¦ macro FY_CPP_MAP2 π¦
FY_CPP_MAP2(a, macro, ...)
Apply a binary macro to every argument, threading a fixed first argument. main character energy: one argument stays the same, the rest get mapped.
π Parameters π
aβ Fixed first argument threaded into every call.macroβ Two-argument macro to apply.- ellipsis (
ellipsis) β Arguments to map over.
π Description π
Expands macro(a, x) for each argument x in __VA_ARGS__. The fixed argument a is passed as the first argument to every invocation.
π macro FY_CONCAT π
FY_CONCAT(_a, _b)
Token-paste two arguments after full macro expansion. no cap, this one actually expands before pasting.
π Parameters π
_aβ Left token (expanded before pasting)._bβ Right token (expanded before pasting).
π Description π
Unlike the raw ## operator, this macro forces both _a and _b to be fully expanded before concatenation, so macro arguments are substituted correctly.
π
macro FY_UNIQUE π
FY_UNIQUE(_base)
Generate a unique identifier using __COUNTER__. iconic for avoiding name collisions.
π Parameters π
_baseβ Identifier prefix.
π Description π
Concatenates _base with the current value of the __COUNTER__ preprocessor counter, which increments by one for each use in a translation unit. Guarantees unique names across multiple macro expansions in the same file.
π«Ά macro FY_LUNIQUE π«Ά
FY_LUNIQUE(_base)
Generate a unique identifier using __LINE__. lowkey the simpler cousin.
π Parameters π
_baseβ Identifier prefix.
π Description π
Like FY_UNIQUE() but uses the current source line number instead of __COUNTER__. Sufficient when only one such identifier is needed per source line; prefer FY_UNIQUE() in general.
π§ macro FY_STACK_SAVE π§
FY_STACK_SAVE(void)
Save the current stack pointer. stack vibes, preserved.
π Parameters π
- void β no arguments
π Description π
On compilers that provide __builtin_stack_save() (GCC, Clang) returns the current stack pointer as a void *, allowing it to be restored later with FY_STACK_RESTORE(). On other compilers returns (void *)NULL.
Use together with FY_STACK_RESTORE() to bound the stack growth of a loop that calls alloca() on each iteration.
π Return π
Opaque stack pointer value, or NULL if unsupported.
π macro FY_STACK_RESTORE π
FY_STACK_RESTORE(_x)
Restore the stack pointer to a previously saved value. rewind time, but for the stack.
π Parameters π
_xβ Value previously returned byFY_STACK_SAVE().
π Description π
On compilers that provide __builtin_stack_restore() rewinds the stack pointer to the value captured by FY_STACK_SAVE(). On other compilers this is a no-op that discards _x.
π macro FY_HAVE_LAMBDAS π
FY_HAVE_LAMBDAS()
Defined when the compiler supports anonymous functions. lambda slay.
π Set when either π
- Clang is compiling with
-fblocks(Blocks extension) β also setsFY_HAVE_BLOCK_LAMBDAS; or - GCC is in use β also sets
FY_HAVE_NESTED_FUNC_LAMBDAS(nested functions).
Code using lambdas should guard against this macro and provide a fallback for environments where it is not defined.
π macro FY_HAVE_BLOCK_LAMBDAS π
FY_HAVE_BLOCK_LAMBDAS()
Defined when Clang Block lambdas are available.
π Description π
Set on Clang when compiled with -fblocks. Implies FY_HAVE_LAMBDAS. Block lambdas use the ^(args){ body } syntax.
π macro FY_HAVE_NESTED_FUNC_LAMBDAS π
FY_HAVE_NESTED_FUNC_LAMBDAS()
Defined when GCC nested-function lambdas are available.
π Description π
Set on GCC. Implies FY_HAVE_LAMBDAS. Nested function lambdas are defined as local functions inside the enclosing function and cannot outlive it.
π₯ macro FY_DIAG_PUSH π₯
FY_DIAG_PUSH()
Save the current diagnostic state onto the compilerβs stack. always a duo with its bestie below.
π Description π
Always paired with FY_DIAG_POP.
π₯ macro FY_DIAG_POP π₯
FY_DIAG_POP()
Restore the diagnostic state saved by the last FY_DIAG_PUSH.
π macro FY_DIAG_IGNORE π
FY_DIAG_IGNORE(_warn)
Suppress a specific warning by pragma string. shhh, compiler, not now.
π Parameters π
_warnβ A string literal suitable for use in a_Pragma()call, e.g."GCC diagnostic ignored \"-Wsomething\"".
πΈ macro FY_DIAG_IGNORE_ARRAY_BOUNDS πΈ
FY_DIAG_IGNORE_ARRAY_BOUNDS()
Suppress -Warray-bounds in the current region.
πΈ macro FY_DIAG_IGNORE_UNUSED_VARIABLE πΈ
FY_DIAG_IGNORE_UNUSED_VARIABLE()
Suppress -Wunused-variable in the current region.
πΈ macro FY_DIAG_IGNORE_UNUSED_PARAMETER πΈ
FY_DIAG_IGNORE_UNUSED_PARAMETER()
Suppress -Wunused-parameter in the current region.
β macro FY_FLT_MANT_DIG β
FY_FLT_MANT_DIG()
Number of base-2 mantissa digits in a float.
π Description π
Typically 24 (IEEE 754 single precision). Fallback: 9.
β macro FY_DBL_MANT_DIG β
FY_DBL_MANT_DIG()
Number of base-2 mantissa digits in a double.
π Description π
Typically 53 (IEEE 754 double precision). Fallback: 17.
β macro FY_LDBL_MANT_DIG β
FY_LDBL_MANT_DIG()
Number of base-2 mantissa digits in a long double.
π Description π
Varies by platform (64-bit x87 extended: 64; MSVC/ARM == double: 53). Fallback: 17.
π macro FY_FLT_DECIMAL_DIG π
FY_FLT_DECIMAL_DIG()
Decimal digits required for a round-trip float.
π Description π
The minimum number of significant decimal digits such that converting a float to decimal and back recovers the original value exactly. Typically 9. Fallback: 9.
π macro FY_DBL_DECIMAL_DIG π
FY_DBL_DECIMAL_DIG()
Decimal digits required for a round-trip double.
π Description π
Typically 17. Fallback: 17.
π macro FY_LDBL_DECIMAL_DIG π
FY_LDBL_DECIMAL_DIG()
Decimal digits required for a round-trip long double.
π Description π
Varies by platform; same as double on most non-x87 targets. Fallback: 17.
π ALLOCATOR π
This header exposes libfyamlβs pluggable allocator subsystem β yes, bestie, you can choose your own memory adventure. Rather than using malloc/free directly, the library routes certain internal allocations through a struct fy_allocator. This lets callers trade memory footprint, speed, and deduplication behaviour to match their workload.
Available strategies (select by name when calling fy_allocator_create()):
"linear"β bump-pointer arena. Allocation is O(1) and near-zero overhead; individual frees are a no-op. Ideal for parse-and-discard workflows where the entire arena is released at once."malloc"β thin wrapper around the systemmalloc/free. Familiar semantics; useful when individual node lifetimes vary. Should never be used for regular application builds. The presence of it is for having a ASAN/valgrind compatible allocator where buffer overflows can be detected easily."mremap"β growable linear arena backed bymremap(2). Avoids copying when the arena needs to grow. Whilemremapis Linux specific, this allocator can be configured to usemmap()ormalloc()areas where they work for other platforms."dedup"β content-addressed store built on xxhash hashing. Stores each unique byte sequence exactly once and returns a shared pointer to all callers. Dramatically reduces memory use when parsing documents with many repeated keys or values (e.g. large YAML configurations, test-suite corpora)."auto"β heuristic selection: given a policy. It usually can do the right thing and is a safe bet.
Tags partition an allocatorβs address space. Obtain a tag with fy_allocator_get_tag() and pass it to every fy_allocator_alloc() / fy_allocator_store() call; release the whole tagβs memory in one shot with fy_allocator_release_tag(). This maps naturally to document lifetimes.
In-place variants (fy_linear_allocator_create_in_place(), fy_dedup_allocator_create_in_place()) initialise the allocator inside a caller-supplied buffer β zero dynamic setup cost, useful in stack-allocated or shared-memory contexts.
πΈ fy_allocator_iterate πΈ
const char *fy_allocator_iterate(const char **prevp)
Iterate over available allocator names. lowkey a little tour of whatβs available.
π Parameters π
prevp(const char**) β The previous allocator iterator pointer
π Description π
This method iterates over all the available allocator names. The start of the iteration is signalled by a NULL in *prevp.
π Return π
The next allocator name in sequence or NULL at the end.
π¦ fy_allocator_is_available π¦
bool fy_allocator_is_available(const char *name)
Check if an allocator is available. vibe check for allocators.
π Parameters π
name(const char*) β The name of the allocator to check
π Description π
Check if the named allocator is available.
π Return π
true if the allocator is available, false otherwise
π fy_allocator_create π
struct fy_allocator *fy_allocator_create(const char *name, const void
*cfg)
Create an allocator. build-a-bear, but for memory strategies.
π Parameters π
name(const char*) β The name of the allocatorcfg(const void*) β The type specific configuration for the allocator, orNULLfor the default.
π Description π
Creates an allocator of the given type, using the configuration argument provided. The allocator may be destroyed by a corresponding call to fy_allocator_destroy().
You can retrieve the names of available allocators with the fy_allocator_get_names() method.
π Return π
A pointer to the allocator or NULL in case of an error.
π
fy_allocator_destroy π
void fy_allocator_destroy(struct fy_allocator *a)
Destroy the given allocator. okay bye, allocator, you served.
a(struct fy_allocator*) β the allocator you're yeeting π
Description
Kills off an allocator you made earlier with fy_allocator_create(). And bestie, tracking allocators are the responsible ones β they release all the memory that was allocated through them. no leaks, no drama, we love a clean exit β¨
fy_linear_allocator_create_in_place
struct fy_allocator *fy_linear_allocator_create_in_place(void *buffer,
size_t size)
Create a linear allocator in place π
Parameters
buffer(void*) β the memory buffer that does double duty as both the storage and the allocator itselfsize(size_t) β how big that memory buffer is
Description
Makes a linear allocator right there in place, using the buffer you handed over. Zero memory allocations happen while doing this β no malloc, no free β so it's totally safe to embed. π«Ά And because it lives in your buffer, there's no need to call fy_allocator_destroy for this one. periodt.
Return
A pointer to the allocator, or NULL if there's no space π
fy_dedup_allocator_create_in_place
struct fy_allocator *fy_dedup_allocator_create_in_place(void *buffer,
size_t size)
Create a dedup allocator in place π¦
Parameters
buffer(void*) β the memory buffer serving as both storage and the allocatorsize(size_t) β the size of that memory buffer
Description
Same deal but dedup-flavored: creates a dedup allocator in place using the buffer you give it. No memory allocations get performed, so it's safe to embed π. And ngl you don't need to call fy_allocator_destroy for this allocator either. One thing to know: the parent allocator of this one will be a linear allocator.
Return
A pointer to the allocator, or NULL if there is no space π
fy_allocator_get_tag
int fy_allocator_get_tag(struct fy_allocator *a)
Get a tag from an allocator π«
Parameters
a(struct fy_allocator*) β the allocator
Description
So the allocator interface is very strict about this: every single allocation has to belong to a tag. This call creates a tag and hands you its value β or an error if it's just not available.
And if an allocator only provides a single tag (the linear allocator is the classic example, iykyk), then the same tag number comes back every time, usually 0.
Return
The created tag, or -1 in case of an error π
fy_allocator_release_tag
void fy_allocator_release_tag(struct fy_allocator *a, int tag)
Release a tag from an allocator ποΈ
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag to release
Description
Releases a tag from an allocator and frees all the memory it allocated β that second part only happens if the allocator actually provides that operation, so check your caps bestie πΈ
fy_allocator_get_tag_count
int fy_allocator_get_tag_count(struct fy_allocator *a)
Get the maximum number of tags an allocator supports π’
Parameters
a(struct fy_allocator*) β the allocator
Description
Tells you the max amount of tags this allocator supports. And if it's a single-tag situation (linear allocator, again, iconic behavior), you get 1 back.
Return
The number of tags, or -1 on error π
fy_allocator_set_tag_count
int fy_allocator_set_tag_count(struct fy_allocator *a, unsigned int
count)
Set the maximum number of tags an allocator supports π§
Parameters
a(struct fy_allocator*) β the allocatorcount(unsigned int) β how many tags the allocator should support
Description
Sets the maximum amount of tags an allocator supports. Heads up though β if the new tag count you're setting is less than the current one, those extra tags get released. poof. gone. π
Return
0 on success, -1 on error
fy_allocator_trim_tag
void fy_allocator_trim_tag(struct fy_allocator *a, int tag)
Trim a tag βοΈ
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag to trim
Description
Trims a tag β meaning it frees any excess memory the allocator is holding onto, snugging it down to fit the size of the content it actually carries. Very "cleaning out my closet" energy π. Allocators that can't do this operation just treat it as a NOP, so nothing breaks.
fy_allocator_reset_tag
void fy_allocator_reset_tag(struct fy_allocator *a, int tag)
Reset a tag π
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag to reset
Description
Resets a tag β frees any content it's carrying, but does not release the tag itself. The tag stays, the contents dip β¨
fy_allocator_alloc
void *fy_allocator_alloc(struct fy_allocator *a, int tag, size_t size,
size_t align)
Allocate memory from an allocator π
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag to allocate fromsize(size_t) β the size of the memory you wantalign(size_t) β the alignment of the object
Description
Grabs memory from the given allocator tag, honoring both the size and align restrictions you asked for.
Return
A pointer to the allocated memory, or NULL π
fy_allocator_free
void fy_allocator_free(struct fy_allocator *a, int tag, void *ptr)
Free memory allocated from an allocator π§Ή
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag that was used to allocate the memoryptr(void*) β pointer to the memory to free
Description
Tries to free memory that was previously handed out by fy_allocator_alloc(). Important caveat bestie: non per-object tracking allocators just treat this as a NOP. So don't count on it actually freeing anything π
fy_allocator_store
const void *fy_allocator_store(struct fy_allocator *a, int tag, const
void *data, size_t size, size_t align)
Store an object to an allocator π¦
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag used to allocate the memorydata(const void*) β pointer to the object you wanna storesize(size_t) β the size of the objectalign(size_t) β the alignment restriction of the object
Description
Stores an object into an allocator and gives you back a pointer to where it landed. And when you're using a deduplicating allocator, no new allocation happens at all β you just get a pointer to the object that was already stored. efficiency who? efficiency HER π
Also: the returned pointer must not be modified. The objects stored are idempotent, periodt.
Return
A constant pointer to the object stored, or NULL in case of an error π
fy_allocator_storev
const void *fy_allocator_storev(struct fy_allocator *a, int tag, const
struct iovec *iov, int iovcnt, size_t align)
Store an object to an allocator (scatter gather) π
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag used to allocate the memory fromiov(const struct iovec*) β the I/O scatter gather vectoriovcnt(int) β the number of vectorsalign(size_t) β the alignment restriction of the object
Description
Stores an object and returns a pointer to where it got stored. With a deduplicating allocator, no new allocation takes place β you get a pointer to the object that's already there. π¦
The object gets created linearly from the scatter gather io vector you provide.
And again, the returned pointer must not be modified β the objects stored are immutable, no cap.
Return
A constant pointer to the object stored, or NULL in case of an error π
fy_allocator_lookup
const void *fy_allocator_lookup(struct fy_allocator *a, int tag, const
void *data, size_t size, size_t align)
Lookup for object in an allocator π
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag used to locate the memorydata(const void*) β pointer to the object to storesize(size_t) β the size of the objectalign(size_t) β the alignment restriction of the object
Description
Searches for the exact contents of an object stored in an allocator and returns a pointer to where it's stored. Big requirement though: the allocator must have the FYACF_CAN_LOOKUP capability. No capability, no lookup, simple as that β¨
Return
A constant pointer to the object stored, or NULL if the object does not exist π
fy_allocator_lookupv
const void *fy_allocator_lookupv(struct fy_allocator *a, int tag, const
struct iovec *iov, int iovcnt, size_t align)
Lookup for object in an allocator (scatter gather) π§
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag used to search intoiov(const struct iovec*) β the I/O scatter gather vectoriovcnt(int) β the number of vectorsalign(size_t) β the alignment restriction of the object
Description
Looks for the exact contents of an object stored in an allocator and returns a pointer to the location where it's stored. Same rule as before: the allocator must have the FYACF_CAN_LOOKUP capability π
The scatter gather vector is what gets used to recreate the object.
Return
A constant pointer to the object stored, or NULL in case the object does not exist π
fy_allocator_dump
void fy_allocator_dump(struct fy_allocator *a)
Dump internal allocator state π©»
Parameters
a(struct fy_allocator*) β the allocator
Description
Spills out the allocator's internal state. Great for when you're like "what is going ON in there" π
enum fy_allocator_cap_flags
enum fy_allocator_cap_flags
Allocator capability flags β the whole personality chart of an allocator π·οΈ
Definition
enum fy_allocator_cap_flags {
FYACF_CAN_FREE_INDIVIDUAL,
FYACF_CAN_FREE_TAG,
FYACF_CAN_DEDUP,
FYACF_HAS_CONTAINS,
FYACF_HAS_EFFICIENT_CONTAINS,
FYACF_HAS_TAGS,
FYACF_CAN_LOOKUP
};
Constants
FYACF_CAN_FREE_INDIVIDUALβ the allocator supports freeing individual allocations π§ΉFYACF_CAN_FREE_TAGβ the allocator supports releasing entire tags ποΈFYACF_CAN_DEDUPβ the allocator supports deduplication π¦FYACF_HAS_CONTAINSβ the allocator can report whether it contains a pointer (even if it does it inefficiently) π’FYACF_HAS_EFFICIENT_CONTAINSβ the allocator can report whether it contains a pointer, and it does it efficiently β‘FYACF_HAS_TAGSβ tells you whether the allocator has individual tags or not π«FYACF_CAN_LOOKUPβ the allocator supports lookup for content π
Description
These flags describe exactly which operations an allocator supports. It's literally giving "what can this allocator do" energy π
fy_allocator_get_caps
enum fy_allocator_cap_flags fy_allocator_get_caps(struct fy_allocator
*a)
Get allocator capabilities π
Parameters
a(struct fy_allocator*) β the allocator
Description
Retrieves the capabilities of an allocator. Vibe check, but for a struct.
Return
The capabilities of the allocator π
π¦ fy_allocator_contains
bool fy_allocator_contains(struct fy_allocator *a, int tag, const void *ptr)
Check if a allocator contains a pointer β literally a vibe check for pointers, bestie. "Is this one of yours or not?"
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag to search in, or-1for all of themptr(const void*) β the object pointer
Description
Reports whether an allocator contains the pointer. No cap, that's the whole job.
Return
true if the pointer ptr is contained in the allocator, false otherwise β¨
πΈ fy_allocator_get_tag_linear_size
ssize_t fy_allocator_get_tag_linear_size(struct fy_allocator *a, int tag)
Get the linear size of an allocator tag. It's giving "how much room would this take if it were all one neat little strip?"
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tag
Description
Retrieves the linear size of the content of a tag. That is the size of a buffer if one was to copy the content of the tag into that buffer in a linear manner. Basically: if you flattened this tag out, how big is it? π
Return
The linear size of the content stored in the tag, or -1 in case of an error.
π fy_allocator_get_tag_single_linear
const void *fy_allocator_get_tag_single_linear(struct fy_allocator *a, int tag, size_t *sizep)
Get the linear extend of a tag. Ok so this one is the "just hand me the whole thing, it's already sitting there" case and honestly? iconic.
Parameters
a(struct fy_allocator*) β the allocatortag(int) β the tagsizep(size_t*) β pointer to a variable that will be filled with the size
Description
If a tag stores its content in a single linear buffer, this retrieves it directly. This is possible only under careful arrangement of allocator configuration, but it is an important optimization case β like, the stars have to align, but when they do? slay π₯
Return
A pointer to the linear content of the tag, or NULL if not.
π§ struct fy_linear_allocator_cfg
linear allocator configuration β the setup sheet for the linear allocator, periodt.
Definition
struct fy_linear_allocator_cfg {
void *buf;
size_t size;
}
Members
bufβ a pointer to a buffer that will be used, orNULLin order to allocatesizeβ size of the buffer in bytes
π enum fy_mremap_arena_type
The mremap allocator arena types. Pick your fighter π
Definition
enum fy_mremap_arena_type {
FYMRAT_DEFAULT,
FYMRAT_MALLOC,
FYMRAT_MMAP
};
Constants
FYMRAT_DEFAULTβ use what's optimal for this platformFYMRAT_MALLOCβ use malloc/realloc arena type (not recommended, bestie, I said what I said π)FYMRAT_MMAPβ use mmap/mremap arena type
β struct fy_mremap_allocator_cfg
mremap allocator configuration β all the knobs, all the drama.
Definition
struct fy_mremap_allocator_cfg {
size_t big_alloc_threshold;
size_t empty_threshold;
size_t minimum_arena_size;
float grow_ratio;
float balloon_ratio;
enum fy_mremap_arena_type arena_type;
}
Members
big_alloc_thresholdβ threshold for immediately creating a new arenaempty_thresholdβ the threshold under which an arena is moved to the full listminimum_arena_sizeβ the minimum (and starting size) of an arenagrow_ratioβ the ratio which an arena will try to grow if full (>1.0)balloon_ratioβ the multiplier for the vm area first allocationarena_typeβ the arena type
Description
If any of the fields is zero, then the system will provide (somewhat) reasonable defaults. "Somewhat" is doing a lot of heavy lifting there and I respect it π
π struct fy_dedup_allocator_cfg
dedup allocator configuration β no duplicates allowed in this group chat π«Ά
Definition
struct fy_dedup_allocator_cfg {
struct fy_allocator *parent_allocator;
unsigned int bloom_filter_bits;
unsigned int bucket_count_bits;
size_t dedup_threshold;
unsigned int chain_length_grow_trigger;
size_t estimated_content_size;
float minimum_bucket_occupancy;
}
Members
parent_allocatorβ the parent allocator (required)bloom_filter_bitsβ number of bits of the bloom filter (or0for default)bucket_count_bitsβ number of bits for the bucket count (or0for default)dedup_thresholdβ number of bytes over which dedup takes place (default0=always)chain_length_grow_triggerβ chain length of a bucket over which a grow takes place (or0for auto)estimated_content_sizeβ estimated content size (or0for don't know)minimum_bucket_occupancyβ the minimum amount that a tag bucket must be full before growth is allowed (default50%, or0.0)
π enum fy_auto_allocator_scenario_type
auto allocator scenario type β choose your adventure, but make it memory management π
Definition
enum fy_auto_allocator_scenario_type {
FYAST_PER_TAG_FREE,
FYAST_PER_TAG_FREE_DEDUP,
FYAST_PER_OBJ_FREE,
FYAST_PER_OBJ_FREE_DEDUP,
FYAST_SINGLE_LINEAR_RANGE,
FYAST_SINGLE_LINEAR_RANGE_DEDUP
};
Constants
FYAST_PER_TAG_FREEβ only per tag freeing, no individual obj freeFYAST_PER_TAG_FREE_DEDUPβ per tag freeing, dedup obj storeFYAST_PER_OBJ_FREEβ object freeing allowed, tag freeing still worksFYAST_PER_OBJ_FREE_DEDUPβ per obj freeing, dedup obj storeFYAST_SINGLE_LINEAR_RANGEβ just a single linear range, no frees at allFYAST_SINGLE_LINEAR_RANGE_DEDUPβ single linear range, with dedup
π struct fy_auto_allocator_cfg
auto allocator configuration β the last little setup sheet in this batch, bestie, stay with me π¦
Definition
struct fy_auto_allocator_cfg {
enum fy_auto_allocator_scenario_type scenario;
size_t estimated_max_size;
}
Members
scenarioβ auto allocator scenarioestimated_max_sizeβ estimated max content size (or0for don't know)
π§΅ THREADING π§΅
Okay bestie, this header is literally giving "I built you a thread pool so you don't have to" energy. It's a simple, portable thread pool built on POSIX threads. It's used internally by the BLAKE3 hasher and by the generic type system's parallel map/filter/reduce operations β and it's also out here as a public API for your application to use. Iconic of it honestly. π
Two operational modes exist, and yes you need to know both:
- Work-stealing mode (
FYTPCF_STEAL_MODE) β the recommended mode for data-parallel loops. You submit a batch of work items withfy_thread_work_join(); the pool distributes items across threads and the caller participates in the execution. About 30% faster than reservation mode for typical workloads. Slay. π₯ - Reservation mode β you explicitly reserve a thread with
fy_thread_reserve(), submit a single work item withfy_thread_submit_work(), continue doing other work in the calling thread, then synchronise withfy_thread_wait_work(). Release the thread afterwards withfy_thread_unreserve().
Three convenience wrappers over fy_thread_work_join() cover the most common data-parallel patterns:
fy_thread_args_join()β array of heterogeneous argument pointersfy_thread_arg_array_join()β flat array of equal-sized argument itemsfy_thread_arg_join()β same argument broadcast to N invocations
An optional fy_work_check_fn callback lets each call site decide at runtime whether a given item is worth offloading to a thread or running inline. vibe check for your workloads, fr fr π
typedef fy_work_exec_fn
void fy_work_exec_fn(void *arg)
Work exec function
Parameters
arg(void*) β The argument to the method
Description
The callback executed on work submission. This is the actual thing that runs, bestie.
typedef fy_work_check_fn
bool fy_work_check_fn(const void *arg)
Work check function
Parameters
arg(const void*) β The argument to the method
Description
Work checker function to decide if it's worth to offload to a thread. Like, is this task even worth the group-chat drama of spawning a thread? That's the question.
Return
true if it should offload to thread, false otherwise.
struct fy_thread_work
Work submitted to a thread for execution
Definition
struct fy_thread_work {
fy_work_exec_fn fn;
void *arg;
struct fy_work_pool *wp;
}
Members
fnβ The execution function for this workargβ The argument to thefnwpβ Used internally, must be set toNULLon entry
Not me warning you aboutwpagain β seriously, set it toNULLon entry or you will have a bad time. π
Description
This is the structure describing the work submitted to a thread for execution.
enum fy_thread_pool_cfg_flags
Thread pool configuration flags
Definition
enum fy_thread_pool_cfg_flags {
FYTPCF_STEAL_MODE
};
Constants
FYTPCF_STEAL_MODEβ Enable steal mode for the thread pool
Description
These flags control the operation of the thread pool. For now only the steal mode flag is defined. Yes, just the one. It's a minimalist era. β¨
struct fy_thread_pool_cfg
thread pool configuration structure.
Definition
struct fy_thread_pool_cfg {
enum fy_thread_pool_cfg_flags flags;
unsigned int num_threads;
void *userdata;
}
Members
flagsβ Thread pool configuration flagsnum_threadsβ Number of threads, if0== online CPUsuserdataβ A userdata pointer
Description
Argument to the fy_thread_pool_create() method. So this is the little bundle of settings you hand over when you're making a pool. π«Ά
fy_thread_pool_create
struct fy_thread_pool *fy_thread_pool_create(const struct fy_thread_pool_cfg *cfg)
Create a thread pool
Parameters
cfg(const struct fy_thread_pool_cfg*) β The configuration for the thread pool
Description
Creates a thread pool with its configuration cfg. The thread pool may be destroyed by a corresponding call to fy_thread_pool_destroy().
Return
A pointer to the thread pool or NULL in case of an error. π
fy_thread_pool_destroy
void fy_thread_pool_destroy(struct fy_thread_pool *tp)
Destroy the given thread pool
Parameters
tp(struct fy_thread_pool*) β The thread pool to destroy
Description
Destroy a thread pool created earlier via fy_thread_pool_create(). Note that this function will block until all threads of the pool are destroyed. So yeah, it waits. Patient queen behavior. π
fy_thread_pool_get_num_threads
int fy_thread_pool_get_num_threads(struct fy_thread_pool *tp)
Get the number of threads
Parameters
tp(struct fy_thread_pool*) β The thread pool
Description
Returns the actual number of created threads.
Return
> 0 for the number of actual threads created, -1 on error.
fy_thread_pool_get_cfg
const struct fy_thread_pool_cfg *fy_thread_pool_get_cfg(struct fy_thread_pool *tp)
Get the configuration of a thread pool
Parameters
tp(struct fy_thread_pool*) β The thread pool
Return
The configuration of the thread pool. Look at her, remembering her own settings. π
fy_thread_reserve
struct fy_thread *fy_thread_reserve(struct fy_thread_pool *tp)
Reserve a thread from the pool.
Parameters
tp(struct fy_thread_pool*) β The thread pool
Description
Reserve a thread from the pool and return it. Note this is only valid for a non-work stealing thread pool. You release the thread again via a call to fy_thread_unreserve.
Return
A reserved thread if not NULL, NULL if no threads are available. π¬
fy_thread_unreserve
void fy_thread_unreserve(struct fy_thread *t)
Unreserve a previously reserved thread
Parameters
t(struct fy_thread*) β The thread
Description
Unreserve a thread previously reserved via a call to fy_thread_reserve(). Note this is only valid for a non-work stealing thread pool. Give her back when you're done, bestie, sharing is caring. π«Ά
fy_thread_submit_work
int fy_thread_submit_work(struct fy_thread *t, struct fy_thread_work *work)
Submit work for execution
Parameters
t(struct fy_thread*) β The threadwork(struct fy_thread_work*) β The work
Description
Submit work for execution. If successful the thread will start executing the work in parallel with the calling thread. You can wait for the thread to terminate via a call to fy_thread_wait_work(). The thread must have been reserved earlier via fy_thread_reserve().
Note this is only valid for a non-work stealing thread pool. (I keep saying it because it keeps mattering. π)
Return
0 if work has been submitted, -1 otherwise.
fy_thread_wait_work
int fy_thread_wait_work(struct fy_thread *t)
Wait for completion of submitted work
Parameters
t(struct fy_thread*) β The thread
Description
Wait until submitted work to the thread has finished. Note this is only valid for a non-work stealing thread pool.
Return
0 if work finished, -1 on error.
fy_thread_pool_are_all_reserved
bool fy_thread_pool_are_all_reserved(struct fy_thread_pool *tp)
Check whether no threads are free
Parameters
tp(struct fy_thread_pool*) β The thread pool
Return
true if all threads are currently reserved, false otherwise. Basically "is the group chat fully booked" check. π΅
fy_thread_pool_is_any_reserved
bool fy_thread_pool_is_any_reserved(struct fy_thread_pool *tp)
Check whether at least one thread is reserved
Parameters
tp(struct fy_thread_pool*) β The thread pool
Return
true if any thread is currently reserved, false otherwise.
fy_thread_work_join
void fy_thread_work_join(struct fy_thread_pool *tp, struct fy_thread_work *works, size_t work_count, fy_work_check_fn check_fn)
Submit works for execution and wait
Parameters
tp(struct fy_thread_pool*) β The thread poolworks(struct fy_thread_work*) β Pointer to an array of works sizedwork_countwork_count(size_t) β The size of theworksarraycheck_fn(fy_work_check_fn) β Pointer to a check function, orNULLfor no checks
Description
Submit works for possible parallel execution. If no offloading is possible at the time, execute in the current context. It is possible to use in both stealing and non-stealing mode with the difference being that stealing mode is about 30% faster. This one's the main character, no cap. β
fy_thread_args_join
void fy_thread_args_join(struct fy_thread_pool *tp, fy_work_exec_fn fn, fy_work_check_fn check_fn, void **args, size_t count)
Execute function in parallel using arguments as pointers
Parameters
tp(struct fy_thread_pool*) β The thread poolfn(fy_work_exec_fn) β The function to execute in parallelcheck_fn(fy_work_check_fn) β Pointer to a check function, orNULLfor no checksargs(void**) β An args array sizedcountof argument pointerscount(size_t) β The count of theargsarray items
Description
Execute fn possibly in parallel using the threads in the thread pool. The arguments of the function are provided by the args array. Heterogeneous pointers, all doing their own thing. π
fy_thread_arg_array_join
void fy_thread_arg_array_join(struct fy_thread_pool *tp, fy_work_exec_fn fn, fy_work_check_fn check_fn, void *args, size_t argsize, size_t count)
Execute function in parallel using argument array
Parameters
tp(struct fy_thread_pool*) β The thread poolfn(fy_work_exec_fn) β The function to execute in parallelcheck_fn(fy_work_check_fn) β Pointer to a check function, orNULLfor no checksargs(void*) β An args array ofargsizeitemsargsize(size_t) β The size of each argument array itemcount(size_t) β The count of theargsarray items
Description
Execute fn possibly in parallel using the threads in the thread pool. The arguments of the function are provided by the args array. Note that here the items are all equal-sized β flat array, uniform vibes, very clean. β¨
fy_thread_arg_join
void fy_thread_arg_join(struct fy_thread_pool *tp, fy_work_exec_fn fn, fy_work_check_fn check_fn, void *arg, size_t count)
Execute function in parallel with the same argument
Parameters
tp(struct fy_thread_pool*) β The thread poolfn(fy_work_exec_fn) β The function to execute in parallelcheck_fn(fy_work_check_fn) β Pointer to a check function, orNULLfor no checksarg(void*) β The common argumentcount(size_t) β The count of executions
Description
Execute fn possibly in parallel using the threads in the thread pool. The argument of the functions is the same. One arg, count executions β broadcast that thing, bestie. π£
π BLAKE3 HASHING π
Okay so this header exposes libfyamlβs embedded BLAKE3 hasher, and BLAKE3 is literally a modern, highly parallelisable cryptographic hash function producing 256-bit (32-byte) output. Iconic behavior.
Three hashing modes are supported, selected via struct fy_blake3_hasher_cfg at creation time:
- Standard β plain BLAKE3 hash, and itβs the default when
keyandcontextareNULL - Keyed β MAC-like hash using a
32-bytekey - Key derivation β derive a subkey from an application context string
The hasher can be used in streaming fashion (update / finalize) or for one-shot hashing of memory regions (fy_blake3_hash()) and files (fy_blake3_hash_file()). File hashing uses mmap by default for large files and can be further parallelised via a thread pool. No cap.
Runtime SIMD backend selection is automatic: the best available backend (SSE2, SSE4.1, AVX2, AVX512 on x86; NEON on ARM; portable C otherwise) is used unless a specific backend name is requested in the config. Use fy_blake3_backend_iterate() to enumerate available backends.
The hasher object is reusable: call fy_blake3_hasher_reset() to start a new hash without reallocating the object. Obsessed.
π§ fy_blake3_backend_iterate π§
const char *fy_blake3_backend_iterate(const char **prevp)
Iterate over the supported BLAKE3 backends.
Parameters
prevp(const char**) β The previous backend pointer, orNULLat start
Description
This method iterates over the supported BLAKE3 backends. The start of the iteration is signalled by a NULL in *prevp.
The default backend is always the last in sequence, so for example if the order is [ βportableβ, βsse2β, NULL ] the default is βsse2β.
Return
The next backend or NULL at the end.
π struct fy_blake3_hasher_cfg π
struct fy_blake3_hasher_cfg β BLAKE3 hasher configuration. Very demure, very mindful.
Definition
struct fy_blake3_hasher_cfg {
const char *backend;
size_t file_buffer;
size_t mmap_min_chunk;
size_t mmap_max_chunk;
bool no_mmap;
const uint8_t *key;
const void *context;
size_t context_len;
struct fy_thread_pool *tp;
int num_threads;
}
Members
backendβNULLfor default, or a specific backend namefile_bufferβ Use this amount of buffer for buffering, zero for defaultmmap_min_chunkβ Minimum chunk size formmapcasemmap_max_chunkβ Maximum chunk size formmapcaseno_mmapβ Disablemmapfor file accesskeyβ pointer to aFY_BLAKE3_KEY_LENarea when in keyed mode.NULLotherwise.contextβ pointer to a context when in key derivation mode.NULLotherwise.context_lenβ The size of the context when in key derivation mode.0otherwise.tpβ The thread pool to use, ifNULL, create a private onenum_threadsβ Number of threads to use:0means default:NUM_CPUS * 3 / 2; a value greater than0means specific number of threads;-1disable threading entirely
Description
Argument to the fy_blake3_hasher_create() method which is the fyamlβs user facing BLAKE3 API. It is very minimal, on purpose, since itβs meant to be exposing a full blown BLAKE3 API. Periodt.
π fy_blake3_hasher_create π
struct fy_blake3_hasher *fy_blake3_hasher_create(const struct fy_blake3_hasher_cfg *cfg)
Create a BLAKE3 hasher object.
Parameters
cfg(const struct fy_blake3_hasher_cfg*) β The configuration for the BLAKE3 hasher
Description
Creates a BLAKE3 hasher with its configuration cfg. The hasher may be destroyed by a corresponding call to fy_blake3_hasher_destroy().
Return
A pointer to the BLAKE3 hasher or NULL in case of an error.
πΈ fy_blake3_hasher_destroy πΈ
void fy_blake3_hasher_destroy(struct fy_blake3_hasher *fyh)
Destroy the given BLAKE3 hasher.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hasher to destroy
Description
Destroy a BLAKE3 hasher created earlier via fy_blake3_hasher_create().
π¦ fy_blake3_hasher_update π¦
void fy_blake3_hasher_update(struct fy_blake3_hasher *fyh, const void *input, size_t input_len)
Update the BLAKE3 hasher state with the given input.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hasherinput(const void*) β Pointer to the inputinput_len(size_t) β Size of the input in bytes
Description
Updates the BLAKE3 hasher state by hashing the given input.
β¨ fy_blake3_hasher_finalize β¨
const uint8_t *fy_blake3_hasher_finalize(struct fy_blake3_hasher *fyh)
Finalize the hash and get output.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hasher
Description
Finalizes the BLAKE3 hasher and returns the output.
Return
A pointer to the BLAKE3 output (sized FY_BLAKE3_OUT_LEN), or NULL in case of an error.
π«Ά fy_blake3_hasher_reset π«Ά
void fy_blake3_hasher_reset(struct fy_blake3_hasher *fyh)
Resets the hasher.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hasher
Description
Resets the hasher for re-use.
π₯ fy_blake3_hash π₯
const uint8_t *fy_blake3_hash(struct fy_blake3_hasher *fyh, const void *mem, size_t size)
BLAKE3 hash a memory area.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hashermem(const void*) β Pointer to the memory to usesize(size_t) β The size of the memory in bytes
Description
Hash a memory area and return the BLAKE3 output.
Return
A pointer to the BLAKE3 output (sized FY_BLAKE3_OUT_LEN), or NULL in case of an error.
π fy_blake3_hash_file π
const uint8_t *fy_blake3_hash_file(struct fy_blake3_hasher *fyh, const char *filename)
BLAKE3 hash a file.
Parameters
fyh(struct fy_blake3_hasher*) β The BLAKE3 hasherfilename(const char*) β The filename
Description
Hash the given file (possibly using mmap).
Return
A pointer to the BLAKE3 output (sized FY_BLAKE3_OUT_LEN), or NULL in case of an error.
β¨ ALIGNMENT β¨
Okay bestie, this header is literally the blueprint for keeping your memory all lined up cute and proper π . It's got portable utilities for working with memory alignment requirements β from compile-time attributes all the way to runtime allocation and pointer-rounding helpers. And the best part? It has zero libfyaml dependencies. Iconic. π
π§ Compile-time attributes
FY_ALIGNED_TO(x)β slaps an alignment attribute on a variable or type; expands to__attribute__((aligned(x)))on GCC/Clang and__declspec(align(x))on MSVCFY_CACHELINE_ALIGNβ shorthand for cache-line (64-byte) alignment, which is super useful for preventing false sharing between fields that different threads access at the same time
π Value rounding
FY_ALIGN(align, x)β rounds an integer up to the next multiple ofalign(must be a power of two)FY_CACHELINE_SIZE_ALIGN(x)β rounds up to the next cache-line boundaryfy_ptr_align(p, align)β rounds a pointer up to alignmentfy_size_t_align(sz, align)β rounds asize_tvalue up to alignment
ποΈ Heap allocation
fy_align_alloc(align, size)/fy_align_free(p)β allocate and free memory with an explicit alignment (posix_memalignon POSIX,_aligned_mallocon Windows)fy_cacheline_alloc(size)/fy_cacheline_free(p)β convenience wrappers that fix the alignment atFY_CACHELINE_SIZE
π§Ί Stack allocation
fy_alloca_align(sz, align)β allocates a block on the stack with a specified alignment; uses plainallocawhen alignment fits withinmax_align_t, otherwise over-allocates and advances the pointer
πΈ macro FY_ALIGNED_TO
FY_ALIGNED_TO(x)
Declare that a variable or type has a minimum alignment. Periodt.
Parameters
- x β Required alignment in bytes (must be a power of two).
Description
Expands to the appropriate compiler-specific alignment attribute:
- GCC/Clang:
__attribute__((aligned(x))) - MSVC: should be
__declspec(align(x))but MSVC does not support trailing alignment⦠- Other: empty (no enforced alignment)
π macro FY_ALIGN
FY_ALIGN(_align, _x)
Round _x up to the next multiple of _align. It's giving "math but make it fashion" π
Parameters
- _align β Alignment boundary (power of two).
- _x β Value to round up.
Description
_align must be a power of two. The result is always >= _x and is the smallest multiple of _align that is >= _x.
Return
_x rounded up to the nearest multiple of _align.
π¦ macro FY_CACHELINE_SIZE
FY_CACHELINE_SIZE()
Size of a CPU cache line in bytes.
Description
Universally 64 bytes on all currently supported architectures (x86, x86_64, ARM, ARM64, PowerPC). No cap, it's just facts.
π macro FY_CACHELINE_SIZE_ALIGN
FY_CACHELINE_SIZE_ALIGN(_x)
Round _x up to the next cache-line boundary.
Parameters
- _x β Value to round up.
Return
_x rounded up to the nearest multiple of FY_CACHELINE_SIZE.
π macro FY_CACHELINE_ALIGN
FY_CACHELINE_ALIGN()
Alignment attribute for cache-line-aligned objects.
Description
Apply this to a variable or struct field to make sure it starts on a cache-line boundary β which prevents false sharing between concurrent readers/writers. Main character energy for your structs, honestly.
Example:
struct my_data {
FY_CACHELINE_ALIGN int hot_counter;
int cold_field;
};
π fy_align_alloc
void *fy_align_alloc(size_t align, size_t size)
Allocate memory with a specific alignment.
Parameters
- align (
size_t) β Required alignment in bytes (must be a power of two and>= sizeof(void *)). - size (
size_t) β Number of bytes to allocate.
Description
Allocates size bytes rounded up to the nearest multiple of align, with the returned pointer guaranteed to be a multiple of align.
Uses posix_memalign() on POSIX systems and _aligned_malloc() on Windows. Free the result with fy_align_free(). Don't forget, bestie π
Return
Pointer to the allocated block, or NULL on failure.
π fy_align_free
void fy_align_free(void *p)
Free memory allocated by fy_align_alloc().
Parameters
- p (
void*) β Pointer previously returned byfy_align_alloc(), orNULL.
Description
A NULL p is silently ignored. Very chill of it.
β fy_cacheline_alloc
void *fy_cacheline_alloc(size_t size)
Allocate cache-line-aligned memory.
Parameters
- size (
size_t) β Number of bytes to allocate.
Description
Equivalent to fy_align_alloc(FY_CACHELINE_SIZE, size). Free the result with fy_cacheline_free().
Return
Cache-line-aligned pointer, or NULL on failure.
πΈ fy_cacheline_free
void fy_cacheline_free(void *p)
Free memory allocated by fy_cacheline_alloc().
Parameters
- p (
void*) β Pointer previously returned byfy_cacheline_alloc(), orNULL.
Description
A NULL p is silently ignored.
π§ fy_ptr_align
void *fy_ptr_align(void *p, size_t align)
Round a pointer up to the next multiple of align.
Parameters
- p (
void*) β Pointer to align. - align (
size_t) β Alignment boundary in bytes (must be a power of two).
Description
Does not allocate any memory; the caller is responsible for ensuring the underlying buffer extends at least (@align - 1) bytes past p. So don't be out here rounding pointers into the void, okay? π
Return
p rounded up to the nearest multiple of align.
π₯ fy_size_t_align
size_t fy_size_t_align(size_t size, size_t align)
Round a size_t value up to the next multiple of align.
Parameters
- size (
size_t) β Value to round up. - align (
size_t) β Alignment boundary in bytes (must be a power of two).
Return
size rounded up to the nearest multiple of align.
π«Ά macro fy_alloca_align
fy_alloca_align(_sz, _align)
Stack-allocate a buffer with a specific alignment.
Parameters
- _sz β Number of bytes to allocate.
- _align β Required alignment in bytes (must be a power of two).
Description
Expands to a statement expression (GCC extension) that allocates _sz bytes on the stack, aligned to _align bytes. When _align <= sizeof(max_align_t) a plain alloca() is used; otherwise _sz + _align - 1 bytes are allocated and the pointer is advanced with fy_ptr_align(). This macro does not work on MSVC. π«
The result is valid only for the lifetime of the enclosing function; do not return or store it beyond that scope. Like, seriously, don't. π
Return
Stack pointer aligned to _align.
π¦ ENDIANNESS π¦
This header is the portable plug for including the platform's byte-order detection headers, and it makes sure the following macros are always defined:
__BYTE_ORDERβ the byte order of the current platform__BIG_ENDIANβ big-endian sentinel value__LITTLE_ENDIANβ little-endian sentinel value
Usage:
#include <libfyaml/fy-internal-endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
// little-endian path
#else
// big-endian path
#endif
Supported platforms:
- Linux / Cygwin / OpenBSD / GNU Hurd / Emscripten β via
<endian.h> - macOS / iOS β via
<libkern/OSByteOrder.h>+<machine/endian.h> - NetBSD / FreeBSD / DragonFly BSD β via
<sys/endian.h> - Windows (MSVC) β via
<winsock2.h>(+<sys/param.h>for MinGW)
The non-standard BYTE_ORDER, BIG_ENDIAN, and LITTLE_ENDIAN spellings are aliased to the double-underscore variants if needed. So you can use the cute short names without fear π
π macro bswap_8
bswap_8(x)
Byte-swap an 8-bit value (no-op).
Parameters
- x β The 8-bit value.
Description
Defined for symmetry with bswap_16(), bswap_32(), and bswap_64(). Swapping a single byte is always a no-op β it's literally just the same byte, bestie. π
Return
x unchanged.
π₯ VARIABLE-LENGTH SIZE ENCODING π₯
This one encodes unsigned integer sizes into a compact, self-delimiting byte stream. The encoding is modelled after the variable-length quantity (VLQ / LEB128 big-endian variant) used in MIDI and other binary formats:
- Each byte carries 7 bits of payload in bits 6..0.
- Bit 7 (MSB) is a continuation flag:
1means more bytes follow,0means this is the last byte. - Exception: the final (maximum-length) byte is always 8 bits of payload with no continuation bit, allowing the full 64-bit / 32-bit range.
64-bit encoding (up to 9 bytes):
bytes bits value range
1 7 0 .. 127
2 14 128 .. 16383
3 21 16384 .. 2097151
4 28 2097152 .. 268435455
5 35 268435456 .. 34359738367
6 42 ..
7 49 ..
8 56 ..
9 64 full uint64_t range
32-bit encoding (up to 5 bytes):
bytes bits value range
1 7 0 .. 127
2 14 128 .. 16383
3 21 16384 .. 2097151
4 28 2097152 .. 268435455
5 32 full uint32_t range (top 4 bits of byte 0 ignored)
The native-width fy_encode_size() / fy_decode_size() family selects the 64-bit or 32-bit variant based on SIZE_MAX. Slay.
Each family provides four operations:
_bytes()β compute the encoded length without writing anythingencode()β write the encoding into a bounded bufferdecode()β read and validate from a bounded bufferdecode_nocheck()β read without bounds checking (caller guarantees room)skip()β advance past an encoded value in a bounded bufferskip_nocheck()β advance without bounds checking
π macro FYVL_SIZE_ENCODING_MAX_64
FYVL_SIZE_ENCODING_MAX_64()
Maximum encoded length of a 64-bit size value.
Description
A 64-bit value requires at most 9 bytes: 8 x 7-bit groups plus one final unconstrained byte = 7 x 8 + 8 = 64 bits.
πΈ macro FYVL_SIZE_ENCODING_MAX_32
FYVL_SIZE_ENCODING_MAX_32()
Maximum encoded length of a 32-bit size value.
Description
A 32-bit value requires at most 5 bytes: 4 x 7-bit groups plus one final unconstrained byte = 7 x 4 + 4 = 32 bits.
π§ fy_encode_size32_bytes
unsigned int fy_encode_size32_bytes(uint32_t size)
Compute the encoded byte count for a 32-bit size.
Parameters
- size (
uint32_t) β The value whose encoded length is queried.
Description
Returns the number of bytes that fy_encode_size32() would write for size, without actually writing anything. Useful for pre-allocating buffers. Big "look before you leap" energy πΈ
Return
Number of bytes required (1β5).
π fy_encode_size32
uint8_t *fy_encode_size32(uint8_t *p, uint32_t bufsz, uint32_t size)
Encode a 32-bit size into a buffer.
Parameters
- p (
uint8_t*) β Start of the output buffer. - bufsz (
uint32_t) β Available space in bytes. size(uint32_t) β the value you wanna encode, bestie π
Description β writes the variable-length encoding of size into the buffer [@p, p+@bufsz) β¨
Return β pointer to one past the last written byte, or NULL if bufsz was too small π
π fy_decode_size32
const uint8_t *fy_decode_size32(const uint8_t *start, size_t bufsz,
uint32_t *sizep)
Decode a variable-length 32-bit size from a buffer. Lowkey essential.
Parameters
start(const uint8_t*) β where the encoded data starts πbufsz(size_t) β how many bytes you're allowed to readsizep(uint32_t*) β output: the decoded value. Set to(uint32_t)-1on error π¨
Description β reads bytes from [@start, start+@bufsz) and reconstructs the encoded 32-bit value. Stops after FYVL_SIZE_ENCODING_MAX_32 bytes at most. No cap, it literally stops π
Return β pointer to one past the last consumed byte, or NULL if the buffer was exhausted before a complete value was found π
π¦ fy_decode_size32_nocheck
const uint8_t *fy_decode_size32_nocheck(const uint8_t *start, uint64_t
*sizep)
Decode a 32-bit size without bounds checking. Living dangerously, iykyk π
Parameters
start(const uint8_t*) β start of the encoded datasizep(uint64_t*) β output: the decoded value as auint64_tfor uniform handling π«Ά
Description β like fy_decode_size32() but assumes the buffer is large enough to hold a complete encoding. The caller must guarantee at least FYVL_SIZE_ENCODING_MAX_32 bytes are available at start. No safety net, bestie β οΈ
Return β pointer to one past the last consumed byte β¨
πΈ fy_skip_size32
const uint8_t *fy_skip_size32(const uint8_t *start, size_t bufsz)
Skip past a variable-length 32-bit size in a buffer. Not me ignoring the value entirely π
Parameters
start(const uint8_t*) β start of the encoded databufsz(size_t) β available bytes
Description β advances past the encoded value without decoding it. Useful when the value itself is not needed. Main character energy: present but not perceived π
Return β pointer to one past the last consumed byte, or NULL if the buffer was exhausted before a complete encoding was found π
π§ fy_skip_size32_nocheck
const uint8_t *fy_skip_size32_nocheck(const uint8_t *p)
Skip a 32-bit encoded size without bounds checking. Fr fr no checks.
Parameters
p(const uint8_t*) β start of the encoded data
Description β like fy_skip_size32() but assumes the buffer is large enough. The caller must guarantee at least FYVL_SIZE_ENCODING_MAX_32 bytes are readable β οΈ
Return β pointer to one past the last consumed byte β¨
π₯ fy_encode_size64_bytes
unsigned int fy_encode_size64_bytes(uint64_t size)
Compute the encoded byte count for a 64-bit size. Just the length, no writing π
Parameters
size(uint64_t) β the value whose encoded length is queried
Description β returns the number of bytes that fy_encode_size64() would write for size, without writing anything. It's giving "look but don't touch" π
Return β number of bytes required (1β9) β
π fy_encode_size64
uint8_t *fy_encode_size64(uint8_t *p, size_t bufsz, uint64_t size)
Encode a 64-bit size into a buffer. The big one.
Parameters
p(uint8_t*) β start of the output bufferbufsz(size_t) β available space in bytessize(uint64_t) β value to encode
Description β writes the variable-length encoding of size into the buffer [@p, p+@bufsz) β¨
Return β pointer to one past the last written byte, or NULL if bufsz was too small π
π fy_decode_size64
const uint8_t *fy_decode_size64(const uint8_t *start, size_t bufsz,
uint64_t *sizep)
Decode a variable-length 64-bit size from a buffer. Obsessed with this one.
Parameters
start(const uint8_t*) β start of the encoded databufsz(size_t) β available bytes to readsizep(uint64_t*) β output: the decoded value. Set to(size_t)-1on error π¨
Description β reads bytes from [@start, start+@bufsz) and reconstructs the encoded 64-bit value. Stops after FYVL_SIZE_ENCODING_MAX_64 bytes at most.
Return β pointer to one past the last consumed byte, or NULL if the buffer was exhausted before a complete value was found π
π fy_decode_size64_nocheck
const uint8_t *fy_decode_size64_nocheck(const uint8_t *start, uint64_t
*sizep)
Decode a 64-bit size without bounds checking. Bestie please read the warning β οΈ
Parameters
start(const uint8_t*) β start of the encoded datasizep(uint64_t*) β output: the decoded value
Description β like fy_decode_size64() but assumes the buffer is large enough. The caller must guarantee at least FYVL_SIZE_ENCODING_MAX_64 bytes are readable π
Return β pointer to one past the last consumed byte β¨
π fy_skip_size64
const uint8_t *fy_skip_size64(const uint8_t *start, size_t bufsz)
Skip past a variable-length 64-bit size in a buffer. Not me skipping the lore π
Parameters
start(const uint8_t*) β start of the encoded databufsz(size_t) β available bytes
Description β advances past the encoded value without decoding it.
Return β pointer to one past the last consumed byte, or NULL if the buffer was exhausted before a complete encoding was found π
π«Ά fy_skip_size64_nocheck
const uint8_t *fy_skip_size64_nocheck(const uint8_t *p)
Skip a 64-bit encoded size without bounds checking. No cap.
Parameters
p(const uint8_t*) β start of the encoded data
Description β like fy_skip_size64() but assumes the buffer is large enough. The caller must guarantee at least FYVL_SIZE_ENCODING_MAX_64 bytes are readable β οΈ
Return β pointer to one past the last consumed byte β¨
β fy_encode_size_bytes
unsigned int fy_encode_size_bytes(size_t size)
Compute encoded byte count for a native size_t. It's giving adaptive π
Parameters
size(size_t) β the value whose encoded length is queried
Description β selects fy_encode_size64_bytes() or fy_encode_size32_bytes() based on SIZE_MAX. So iconic of it π
Return β number of bytes required.
π fy_encode_size
uint8_t *fy_encode_size(uint8_t *p, size_t bufsz, size_t size)
Encode a native size_t into a buffer. Native and thriving.
Parameters
p(uint8_t*) β start of the output bufferbufsz(size_t) β available space in bytessize(size_t) β value to encode
Description β selects fy_encode_size64() or fy_encode_size32() based on SIZE_MAX.
Return β pointer to one past the last written byte, or NULL on overflow π
π¦ fy_decode_size
const uint8_t *fy_decode_size(const uint8_t *start, size_t bufsz, size_t
*sizep)
Decode a native size_t from a buffer. We love a portable moment.
Parameters
start(const uint8_t*) β start of the encoded databufsz(size_t) β available bytessizep(size_t*) β output: the decoded value
Description β selects fy_decode_size64() or fy_decode_size32() based on SIZE_MAX.
Return β pointer to one past the last consumed byte, or NULL on error π
π§ fy_decode_size_nocheck
const uint8_t *fy_decode_size_nocheck(const uint8_t *start, size_t
*sizep)
Decode a native size_t without bounds checking. No thoughts, just vibes (and caller guarantees) π
Parameters
start(const uint8_t*) β start of the encoded datasizep(size_t*) β output: the decoded value
Description β selects the 64-bit or 32-bit nocheck variant based on sizeof(size_t). The caller must guarantee enough bytes are available at start. Please don't make it a whole situation β οΈ
Return β pointer to one past the last consumed byte β¨
πΈ fy_skip_size
const uint8_t *fy_skip_size(const uint8_t *start, size_t bufsz)
Skip a native size_t encoding in a buffer. Ghosting it, respectfully.
Parameters
start(const uint8_t*) β start of the encoded databufsz(size_t) β available bytes
Description β selects fy_skip_size64() or fy_skip_size32() based on SIZE_MAX.
Return β pointer to one past the last consumed byte, or NULL on error π
π fy_skip_size_nocheck
const uint8_t *fy_skip_size_nocheck(const uint8_t *start)
Skip a native size_t encoding without bounds checking. Reckless but make it fashion π
Parameters
start(const uint8_t*) β start of the encoded data
Description β selects the 64-bit or 32-bit nocheck variant based on SIZE_MAX. The caller must guarantee enough bytes are available β οΈ
Return β pointer to one past the last consumed byte β¨
π§ macro FYVL_SIZE_ENCODING_MAX
FYVL_SIZE_ENCODING_MAX()
Maximum encoded length for a native size_t. The ceiling, literally.
Description β equals FYVL_SIZE_ENCODING_MAX_64 on 64-bit platforms, FYVL_SIZE_ENCODING_MAX_32 on 32-bit platforms π
βοΈ ATOMIC OPERATIONS βοΈ
Okay so this header is giving thin, portable abstraction over C11 <stdatomic.h> with a graceful fallback for compilers that support _Atomic as an extension (GCC without -std=c11, Clang) and a last-resort non-atomic fallback for toolchains that support neither. Very βI got you, bestieβ energy π.
These detection macros are defined by this header and are not meant for direct use.
FY_HAVE_STDATOMIC_Hβ<stdatomic.h>was successfully included; the standardatomic_*functions and types are available.FY_HAVE_C11_ATOMICSβ the_Atomicqualifier is available, either from<stdatomic.h>or as a compiler extension.FY_HAVE_ATOMICSβ effective atomics are available (_Atomicmaps to a real atomic type). When not defined,_Atomic(_x)expands to plain_xand all operations are non-atomic single-threaded stubs.FY_HAVE_SAFE_ATOMIC_OPSβ the underlying operations are properly memory-ordered (i.e.<stdatomic.h>is in use). Without this, operations are performed as plain loads/stores with no memory barriers.
β¨ Public API β all fy_atomic_* macros delegate to the selected backend
FY_ATOMIC()β qualify a type as atomicfy_atomic_flagβ boolean flag typefy_atomic_load()/fy_atomic_store()fy_atomic_exchange()fy_atomic_compare_exchange_strong()/fy_atomic_compare_exchange_weak()fy_atomic_fetch_add()/fy_atomic_fetch_sub()fy_atomic_fetch_or()/fy_atomic_fetch_xor()/fy_atomic_fetch_and()fy_atomic_flag_clear()/fy_atomic_flag_set()/fy_atomic_flag_test_and_set()fy_cpu_relax()β emit a CPU relaxation hint (PAUSE/YIELD)fy_atomic_get_and_clear_counter()β atomically read and subtract a counter
π macro FY_ATOMIC
FY_ATOMIC(_x)
Qualify a type as atomic. Iconic behavior, honestly.
Parameters
_xβ The underlying C type.
Description
Expands to _Atomic(_x) when FY_HAVE_ATOMICS is defined, or to plain _x otherwise (non-atomic fallback).
Example:
FY_ATOMIC(uint64_t) refcount;
π¦ macro fy_atomic_flag
fy_atomic_flag()
A boolean flag that can be set/cleared/tested atomically.
Description
Backed by atomic_flag from <stdatomic.h> when available, or a plain bool in the fallback path.
πΈ macro fy_atomic_load
fy_atomic_load(_ptr)
Atomically load the value at _ptr.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified variable.
Return
The current value.
π
macro fy_atomic_store
fy_atomic_store(_ptr, _v)
Atomically store _v at _ptr.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified variable._vβ Value to store.
π₯ macro fy_atomic_exchange
fy_atomic_exchange(_ptr, _v)
Atomically replace the value at _ptr with _v.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified variable._vβ New value to store.
Return
The old value that was at _ptr before the exchange.
π macro fy_atomic_compare_exchange_strong
fy_atomic_compare_exchange_strong(_ptr, _e, _d)
Strong CAS: replace _ptrβs value if it equals _e.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified variable._eβ Pointer to the expected value (updated on failure)._dβ Desired value to store on success.
Description
If *_ptr == *_e, stores _d into _ptr and returns true. Otherwise, loads the current value into _e and returns false. The strong variant never spuriously fails.
Return
true if the exchange succeeded, false otherwise.
π macro fy_atomic_compare_exchange_weak
fy_atomic_compare_exchange_weak(_ptr, _e, _d)
Weak CAS: may spuriously fail. Not flaky β just built different π.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified variable._eβ Pointer to the expected value (updated on failure)._dβ Desired value to store on success.
Description
Like fy_atomic_compare_exchange_strong() but may fail even when *_ptr == *_e. Prefer in retry loops where a spurious failure is harmless and performance matters.
Return
true if the exchange succeeded, false otherwise.
π macro fy_atomic_fetch_add
fy_atomic_fetch_add(_ptr, _v)
Atomically add _v to _ptr and return the old value.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified integer variable._vβ Value to add.
Return
The value of _ptr before the addition.
π§ macro fy_atomic_fetch_sub
fy_atomic_fetch_sub(_ptr, _v)
Atomically subtract _v from _ptr and return the old value.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified integer variable._vβ Value to subtract.
Return
The value of _ptr before the subtraction.
π macro fy_atomic_fetch_or
fy_atomic_fetch_or(_ptr, _v)
Atomically OR _v into _ptr and return the old value.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified integer variable._vβ Value to OR in.
Return
The value of _ptr before the operation.
β macro fy_atomic_fetch_xor
fy_atomic_fetch_xor(_ptr, _v)
Atomically XOR _v into _ptr and return the old value.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified integer variable._vβ Value to XOR in.
Return
The value of _ptr before the operation.
π«Ά macro fy_atomic_fetch_and
fy_atomic_fetch_and(_ptr, _v)
Atomically AND _v into _ptr and return the old value.
Parameters
_ptrβ Pointer to anFY_ATOMIC-qualified integer variable._vβ Value to AND in.
Return
The value of _ptr before the operation.
π macro fy_atomic_flag_clear
fy_atomic_flag_clear(_ptr)
Atomically clear a flag (set to false).
Parameters
_ptrβ Pointer to anfy_atomic_flag.
π macro fy_atomic_flag_set
fy_atomic_flag_set(_ptr)
Atomically set a flag (set to true).
Parameters
_ptrβ Pointer to anfy_atomic_flag.
Note
This is a libfyaml extension; standard <stdatomic.h> only provides atomic_flag_test_and_set(). In the fallback path this is implemented as a plain store.
π¦ macro fy_atomic_flag_test_and_set
fy_atomic_flag_test_and_set(_ptr)
Atomically set a flag and return its old value.
Parameters
_ptrβ Pointer to anfy_atomic_flag.
Description
Sets the flag to true and returns the value it held before the operation. This is the standard test-and-set primitive.
Return
true if the flag was already set, false if it was clear.
π« fy_cpu_relax
void fy_cpu_relax(void)
Emit a CPU relaxation hint inside a spin-wait loop.
Parameters
voidβ no arguments
Description
Reduces power consumption and improves hyper-threading performance on x86/x86_64 (PAUSE), signals a yield on AArch64/ARM (YIELD), and emits a low-priority hint on PowerPC (or 27,27,27). Falls back to a compiler memory barrier on unsupported architectures.
Use inside tight spin loops to avoid memory-ordering penalties and allow sibling hardware threads to make progress:
while (!fy_atomic_load(&ready))
fy_cpu_relax();
π COPYRIGHT π
Copyright 2019-2026, Pantelis Antoniou β periodt, protect the whole vibe.
And thatβs the footer tea, bestie: this page is LIBFYAML-MISC(3), dated Mar 15, 2026. Go forth and atomically slay π
β¨
π¦ where this page comes from
- nixpkgs unstable: libfyaml 0.9.6
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms