Skip to content

proven_c_lib-v0.2.0

Choose a tag to compare

@rubidus-api rubidus-api released this 28 Sep 03:57
· 52 commits to main since this release

A MINOR release: new public API, nothing removed. UTF-16 text gets a way in and out and UTF-8
shows correctly on a Windows console (B-039); the view vocabulary - split, trim, affixes,
find_last, contains, ordering (RFC-0005, B-018 to B-022); an allocator wrapper that catches the
wrong allocator at the call (B-040); Windows symlinks and the 4 GiB entropy boundary measured and
fixed (B-033); reproducible manual PDFs. Existing behaviour changes only where it was wrong: on a
Windows console, for Windows symlinks, and in proven_time_u16_fmt with non-ASCII locales.

Added

  • utf.h: strict UTF-8 <-> UTF-16 transcoding. Measuring (proven_utf8_to_utf16_size,
    proven_utf16_to_utf8_size), fixed-capacity all-or-nothing (proven_utf8_to_utf16,
    proven_utf16_to_utf8), partial for text read in pieces (proven_utf8_to_utf16_partial,
    proven_utf16_to_utf8_partial, reporting proven_utf_step_t), and growable all-or-nothing
    (proven_utf8_append_to_u16str, proven_utf16_append_to_u8str). Malformed input is always
    PROVEN_ERR_INVALID_ENCODING - overlongs, encoded surrogates, values above U+10FFFF, stray
    continuations, unpaired surrogates - and nothing is repaired. Input cut mid-character is
    PROVEN_ERR_NEED_MORE in the partial forms, malformed in the whole forms.

  • u16 text through the formatter. proven_arg_u16, and PROVEN_ARG on a
    proven_u16str_view_t, render UTF-8 into every formatter sink: proven_println,
    proven_eprintln, proven_fprintln into any writer, proven_u8str_append_fmt*. Width counts
    UTF-8 bytes as for a u8 view; an unpaired surrogate fails the format.

  • u16 text through writers and readers (stream.h). proven_writer_write_u16 writes UTF-8,
    UTF-16LE or UTF-16BE (proven_text_encoding_t), validated before anything is written;
    proven_writer_write_bom writes a byte order mark only on request. proven_u16_reader_t
    (proven_u16_reader_init, _read_line, _read) decodes any of the three encodings from any
    reader into a caller-owned buffer of code units, with PROVEN_TEXT_AUTO choosing by BOM,
    carrying a character split across reads, and keeping the byte line reader's newline, full-buffer
    and last-line rules.

  • sysio u16 line input: proven_sysio_u16_lines_open, proven_sysio_stdin_u16_lines,
    proven_sysio_read_u16_line, with proven_sysio_u16_lines_t. proven_result_u16str_view_t in
    u16str.h.

  • PAL: proven_sys_io_is_console, proven_sys_io_console_write_u16,
    proven_sys_io_console_read_u16 (Windows; POSIX answers "not a console" / unsupported).

  • Manual: chapter 3 "Converting between UTF-8 and UTF-16", chapter 5 "UTF-16 text in and out,
    and the Windows console", both editions, with runnable examples ex_03_utf and ex_05_u16_io.

  • b039-console-check.c and build-b039-check.sh: a native Windows check that
    makes its own console in code page 949 and verifies output by reading the screen buffer back
    and input by injecting key events.

  • alloc_check.h: an allocator that knows its own blocks (B-040). proven_alloc_check_wrap
    puts a checker in front of any allocator and records the blocks it hands out in caller-supplied
    memory; a foreign free, a double free, a realloc of a foreign block or with the wrong old size or
    alignment, and an allocation past the record are refused with proven_panic at the call, and the
    refused pointer never reaches the inner allocator. proven_alloc_checked wraps only where
    PROVEN_ALLOC_CHECK is defined (before the first proven header, e.g. -D) and is otherwise the
    identity - it is a testing and debugging tool, and the lookup is linear. proven_alloc_check_owns,
    proven_alloc_check_live (a leak check). Tests test_unit_alloc_check, test_unit_alloc_check_on;
    manual chapter 2 section 7 with ex_02_alloc_check, both editions.

  • The view vocabulary (RFC-0005; B-018 to B-022). In u8str.h, all pure and non-allocating,
    ill-formed views treated as empty, every empty result {NULL, 0}:
    proven_u8str_view_split / _split_next with proven_u8str_view_split_t (n separators yield
    n + 1 fields; an empty separator yields the input once; the iterator is copyable);
    proven_u8str_view_trim, _trim_start, _trim_end (exactly six ASCII whitespace bytes);
    proven_u8str_view_remove_prefix / _remove_suffix (unchanged when absent);
    proven_u8str_view_find_last (last start position, overlaps counted; size for an empty needle;
    byte scan / backward Shift-Or / repeated forward search by needle length) and
    proven_u8str_view_contains; proven_u8str_view_cmp / _cmp_ptr (bytewise unsigned, prefix
    first, sign only); proven_u8str_view_is_well_formed. Tests test_unit_u8str_view_cmp,
    test_unit_u8str_view_ops, test_unit_u8str_split, test_regression_split_empty_sep,
    test_differential_find_last_oracle (60,000 cases; planted defects caught). Manual chapter 3
    section 1 in both editions with ex_03_view_ops. The RFC-0004 benchmark now measures the
    shipped iterator: 18.1 ns/field against 16.5 for a correct hand-rolled loop (median of three).

Changed

  • On a Windows console, sysio writes and reads UTF-16. proven_print/proven_eprint, the
    stdout/stderr writers, the stdin reader, proven_sysio_*_buffered, the u8 and u16 line
    readers and proven_sysio_scanner_t detect a console once (GetConsoleMode) and use
    WriteConsoleW/ReadConsoleW, converting at the edge. Before, UTF-8 was handed to the
    console with WriteFile and shown in the console's code page - mojibake under 949 unless
    chcp 65001 had been run - and console input came back in that code page. The console's code
    page is not changed. Malformed UTF-8 sent to a console is refused after the valid part; a
    character split across buffered flushes is carried in the state struct; Ctrl+Z at the start of
    a console line is end of input. Files, pipes and redirected streams stay byte-exact; POSIX is
    unchanged. proven_writer_from_file on a console handle stays byte-exact, as documented.

  • The allocator pairing of owned strings is now stated as a warning (B-023, owner decision
    2026-09-28): u8str.h/u16str.h and manual chapter 3 say that the string does not remember its
    allocator and nothing checks it, with a counter-example. No field was added; an allocator-side
    ownership check is proposed as B-040.

  • proven_sysio_std_t gains console and carry (proven_sysio_carry_t);
    proven_sysio_scanner_t gains the same two fields. Layout change for code that declares them.

  • The manual PDFs are reproducible. scripts/build-site.sh sets SOURCE_DATE_EPOCH from the
    commit being built (a caller's own value wins), so the same commit gives byte-identical PDFs -
    two full builds matched by SHA-256 for both editions; before, they matched only in size.
    scripts/release.sh now compares an existing release asset with the built one by SHA-256
    (GitHub's asset digest, or the downloaded asset), not by size.

Fixed

  • Code review of the unreleased work (2026-09-28), ten findings:
    • A buffered writer's automatic drain flushed the inner writer, and on a Windows console that
      flush ended the text: valid UTF-8 split at a buffer boundary came back INVALID_ENCODING with
      bytes lost. Drains no longer flush the inner writer, and the console writer's flush keeps an
      open character for the next write. Reproduced on the Win11 VM before (FAIL) and fixed after
      (win64/win32 18/18).
    • A non-NULL empty view passed through remove_prefix, remove_suffix and split as {p, 0};
      every empty result is now {NULL, 0} as documented.
    • The Windows symlink kind is decided from a path normalised before any \\?\ prefix.
      Defensive: the long-path case the review predicted did not fail on Windows 11 before the fix.
    • proven_u16_reader_t stages 1 KiB with a cursor: about one source read per KiB instead of
      one per 64 bytes.
    • utf.h's append functions grow once and convert in place (no chunk copy, no rollback).
    • One overlap rule (proven_range_overlaps) for u16 input in utf.h and the formatter; the
      formatter used to accept a view running into its output from below.
    • One padding rule (spec_padding) for plain, custom and UTF-16 fields.
    • build-b033-check.sh prints its report and cleans up when the check fails; release.sh
      never deletes an asset because a digest download failed.
    • New comments are ASCII.
  • Windows symlinks (B-033). proven_fs_symlink created a link to a directory as a file
    link, which cannot be listed or entered, and made every relative target absolute against the
    current directory (sub/rel -> t pointed at ./t), because the target went through the
    helper that calls GetFullPathNameW. The target is now stored as written (with / as \),
    the directory flag follows the target as the link resolves it, older Windows without
    ALLOW_UNPRIVILEGED_CREATE is retried, and failures are PROVEN_ERR_PERMISSION or
    PROVEN_ERR_NOT_FOUND where they can be told apart (POSIX too), not always PROVEN_ERR_IO.
    Measured on the Win11 VM before (7 of 12 failed) and after (win64 12/12, win32 11/11) with
    b033-windows-check.c, which also filled a 4 GiB + 4 KiB entropy request across the
    32-bit count boundary.
  • proven_time_u16_fmt widened each UTF-8 byte into a code unit. A caller-supplied locale
    with non-ASCII names produced three meaningless units per Hangul syllable; it now transcodes.
    Reproduced red first in tests/test_unit_time_fmt_u16_parity.

Verification

  • New tests: test_unit_utf (every scalar value; UTF-8 validity against an independent
    formulation over every 1-3 byte input; planted defects caught), test_unit_stream_u16,
    test_unit_sysio_console (a fake console at every split offset and read size). Debug build:
    209 executables. Windows 11 VM, 2026-09-27: b039-check-win64.exe and -win32.exe 17/17
    each - console output under code page 949, injected console input, pipes and files. ./nob cross on arch-dev: all 11 targets. Not measured: console input typed through an IME by a
    person; a legacy (pre-Windows 10) console host.