# 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;
}
```

------------------------------------------------------------
