AxlFormat — Printf Engine
AxlFormat — Printf Engine
Callback-driven printf engine. Format text directly into any sink (buffer, network socket, file, hash) without intermediate allocation.
This is the engine behind axl_printf, axl_snprintf, axl_asprintf,
and axl_string_append_printf. It has zero dependencies (no memory
allocator, no I/O) — it breaks the Log -> Data circular dependency by
being self-contained.
Header: <axl/axl-format.h>
Callback-Driven Formatting
The core API takes a write callback that receives formatted output in chunks. No memory is allocated — all formatting uses a small stack buffer.
#include <axl.h>
// Write directly to a TCP socket
void net_write(const char *data, size_t len, void *ctx) {
axl_tcp_send((AxlTcp *)ctx, data, len, 0);
}
// Format an HTTP request line directly into the socket
axl_format(net_write, sock, "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n",
path, host);
API
Two functions:
axl_format(write_fn, ctx, fmt, ...)— variadic convenienceaxl_vformat(write_fn, ctx, fmt, args)— va_list version
The callback type:
typedef void (*AxlWriteFunc)(const char *data, size_t len, void *ctx);
Use Cases
Format directly into a network send buffer (no intermediate string)
Stream formatted output into a hash computation
Write a custom logging backend that formats in-place
Build protocol messages without allocating temporary strings
Supported Format Specifiers
Specifier |
Type |
Example |
|---|---|---|
|
|
|
|
signed int |
|
|
unsigned int |
|
|
hex (lower/upper) |
|
|
|
|
|
|
|
|
char |
|
|
pointer |
|
|
literal |
|
Width and zero-padding are supported: %08x, %-20s, %5d.
API Reference
Defines
-
AXL_DTOA_BUF_MIN
Minimum
bufszfor axl_dtoa: the shortest decimal representation of any IEEE-754 double needs at most 17 significant digits; +1 for the NUL terminator axl_dtoa writes.
Typedefs
-
typedef void (*AxlWriteFunc)(const char *data, size_t len, void *ctx)
axl-format.h:
Callback-driven printf engine. Format text directly into any sink (buffer, network socket, file, hash) without intermediate allocation.
This is the engine behind axl_printf, axl_snprintf, axl_asprintf, and axl_string_append_printf. Exposed for consumers who need custom formatting targets.
Supports: d i u x X s c p %% Length modifiers: l ll z Flags: 0 - + (space) Width: N or * Precision: .N or .* AxlWriteFunc:
Callback invoked by the format engine to emit output. May be called multiple times per format call (once per literal segment and once per formatted argument).
Functions
-
void axl_vformat(AxlWriteFunc write_fn, void *ctx, const char *fmt, va_list args)
Format with va_list into a write callback.
The engine calls
write_fnone or more times with formatted output segments. No memory is allocated — all formatting uses a small stack buffer.void my_write(const char *data, size_t len, void *ctx) { axl_tcp_send((AxlTcp *)ctx, data, len, 0); } va_list args; va_start(args, fmt); axl_vformat(my_write, sock, fmt, args); va_end(args);
- Parameters:
write_fn – output callback
ctx – passed to write_fn
fmt – printf-style format string
args – format arguments
-
void axl_format(AxlWriteFunc write_fn, void *ctx, const char *fmt, ...)
Format into a write callback (variadic wrapper).
Convenience wrapper around axl_vformat.
void buf_write(const char *data, size_t len, void *ctx) { // append to a custom buffer } axl_format(buf_write, &my_buf, "count=%d name=%s", 42, "AXL");
- Parameters:
write_fn – output callback
ctx – passed to write_fn
fmt – printf-style format string
- Param :
format arguments
-
int axl_dtoa(double value, char *buf, size_t bufsz, int *out_decpt, int *out_neg)
Shortest round-trippable decimal digits of a double (Grisu2).
Converts the finite double
valueto the shortest string of decimal digits that, when read back, reproducesvalueexactly (round-trip). This is the engine behind f / e / g and the primitive a consumer needs to serialize a double without losing precision.Output is split into three pieces so the caller can render any C float format from one conversion:
bufreceives the significant digits as ASCII ‘0’..’9’, with no sign, no decimal point, and no exponent. NUL-terminated.out_decptreceives the position of the decimal point measured in digits from the start ofbuf:the value’s magnitude is0.<digits> x 10^(*out_decpt)… equivalently<digits-as-integer> x 10^(*out_decpt - ndigits). So *out_decpt is the count of digits that belong to the left of the decimal point (it may be <= 0 or > ndigits). Examples: 1.5 -> “15”, decpt 1; 0.001 -> “1”, decpt -2; 100.0 -> “1”, decpt 3.out_negreceives true for a negativevalue(including -0.0), false otherwise.
Zero yields “0” with
*out_decpt== 1. The result is the canonical shortest form: trailing zeros are not emitted (100.0 is “1” with decpt 3, not “100”).valueMUST be finite. NaN and +/-infinity are NOT handled here (callers detect them first —v != vfor NaN,|v| > DBL_MAXfor infinity); passing one returns 0.No allocation, no libm, no libc. Uses a ~1.3KB cached-powers table.
- Parameters:
value – finite value to convert
buf – [out] digit buffer (>= AXL_DTOA_BUF_MIN bytes)
bufsz – size of
bufout_decpt – [out] decimal-point position (NULL OK)
out_neg – [out] 1 if negative (NULL OK)
- Returns:
number of digits written to
buf(>= 1), or 0 on error (bufNULL,bufsz< AXL_DTOA_BUF_MIN, orvaluenon-finite).out_decpt/out_negmay be NULL to skip.