A MINOR release: nothing public removed. It carries the whole-library review (RFC-0009): defects
fixed, four security changes, faster CRC-32, SHA-256, directory listing and stream readers, map
iteration, in-place array editing, job groups, and a build that links its tests in parallel.
Behaviour changes, each where the old behaviour was wrong or unsafe: an exclusive create over an
existing name is PROVEN_ERR_EXISTS instead of PROVEN_ERR_IO; environment values and directory
entry names that are not valid text are PROVEN_ERR_INVALID_ENCODING on POSIX too (they were
returned as raw bytes; Windows substituted U+FFFD); proven_map_hash values for default
integer-key maps change and now differ per process; and proven_reader_buffered_t gains a field.
Added
PROVEN_ERR_EXISTS: an exclusive create found the name already there (RFC-0009 X-009).
proven_fs_openwithPROVEN_FS_CREATE_NEWover an existing name returnedPROVEN_ERR_IO,
the same code as every unclassified failure, so a caller could not tell "choose another name"
from "the disk failed". It now returns the new code, on POSIX (EEXIST) and Windows
(ERROR_FILE_EXISTS).PROVEN_ERR_LASTmoves to it; aliasXCV_ERR_EXISTS. Code that
compared that case againstPROVEN_ERR_IOmust compare againstPROVEN_ERR_EXISTS.proven_fs_is_staging_name(name): true for a name with the shape of the temp file an
atomic or durable write leaves behind when killed mid-write (today's and the old one), for a
cleanup job. The library itself never removes one it did not just create (RFC-0009 D-001).proven_random_u64_checked(&out): one strong word, andfalsewhen there is none
(RFC-0009 S-002).proven_random_u64returns 0 when the entropy source fails - on a bare-metal
target with no source that is every call - and its documentation offered it for tokens. A
token of 0 is a predictable secret nothing reports. The checked form is for secrets; the
unchecked one stays, now documented as not for them. Aliasxcv_random_u64_checked.proven_fs_read_all_bounded(alloc, path, max_bytes): a whole-file read with a ceiling
(RFC-0009 S-003).proven_fs_read_allreads until the source ends, so a path naming
/dev/zero, an endless FIFO, or a far larger file than expected grows the buffer until the
allocator refuses. The bounded form returnsPROVEN_ERR_OUT_OF_BOUNDSand no buffer past
max_bytes: before allocating when the reported size says so, and as the extra byte arrives
when the size says nothing. The unbounded functions now say they are for trusted paths. Alias
xcv_fs_read_all_bounded.- A map can be walked:
proven_map_iter_init/proven_map_iter_next, andproven_map_len
(RFC-0009 X-001). Before, a program could not list what it stored, serialise a map, or release
what its values own without keeping a second list of keys. The walk is in bucket order; removing
entries (including the current one) and updating values are allowed during it; a new key or a
reserve that rehashes the map makes the next stepPROVEN_ERR_INVALID_STATEinstead of skipping
or repeating entries. Aliasesxcv_map_iter_t,xcv_map_iter_init,xcv_map_iter_next,
xcv_map_len. - Arrays can be edited in place:
proven_array_clear,_truncate,_insert,_remove_at,
_swap_removeand_extend(RFC-0009 X-002) - the operations callers wrote by hand on top
ofget_mutandlen. Each changes nothing when it fails;insertandextendaccept
elements taken from the array itself (a source that only partly overlaps it is refused), and
extendreallocates at most once. Aliasesxcv_array_*. - Jobs:
proven_job_submit_exsays why a submit was refused, and job groups can be waited on
(RFC-0009 X-003).proven_job_submitreturnsfalseboth for a full queue and for a closed
system, which call for opposite responses;_exreturnsPROVEN_ERR_AGAINor
PROVEN_ERR_INVALID_STATE.proven_job_group_tcounts submitted jobs
(proven_job_group_init,_submit,_pending);proven_job_group_waitreturns when all have
run, running queued jobs itself while it waits, with what they wrote visible afterwards.
Aliasesxcv_job_submit_ex,xcv_job_group_*.
Changed
-
SHA-256 is about 25% faster (RFC-0009 P-107):
proven_sha256_updatecompresses whole
64-byte blocks straight from the input instead of copying every byte into its block buffer
first. Measured on 4 MiB: 4.7 ns per byte, from 6.2. Digests are unchanged. -
./nob build -keep-goingruns every test and lists the failures (RFC-0009 X-006, first part).
A run stopped at the first failing test, so one failure hid every other. With the flag, each
test still runs once and the run ends with a summary and exit status 1. -
./nob <mode> -jobs Nruns tests in parallel on POSIX (RFC-0009 X-006). Tests created
fixtures under fixed names in the working directory, so they could only run one at a time. With
the flag each runs in a directory of its own that links to the repository's top level, its output
is printed whole in registry order, and stress and benchmark tests still run alone. On the
16-CPU host a cached./nob asantakes 8.9 s instead of 16.9, a cached./nob build7.1 s
instead of 10.1. The default stays one test at a time. -
A clean
./nob buildtakes 14 s instead of 36 on a 16-CPU host (RFC-0009 P-108). The test
executables were compiled and linked one at a time - 20 of the 31 seconds a clean build spent
outside the tests themselves. They are now linked in parallel, up to one per CPU, and then run
one at a time in registry order as before, with a link failure still reported under the test's
name. Running the tests in parallel needs per-test scratch files first and is not done. -
The seven
*_internal/*_implfunctions in public headers are marked MACRO SUPPORT and are
not a stable interface (RFC-0009 X-004):proven_u8str_fmt_internal,proven_scan_fmt_internal,
proven_scan_fmt_internal_view,proven_fmt_to_writer_impl,proven_sysio_scanner_scan_impl,
proven_sysio_print_impl,proven_sysio_scan_chunk_impl. Their signatures may change in a MINOR
release; call the macros. A source-contract test keeps the list closed. -
proven_u16_reader_readwith a small destination no longer revalidates everything it holds
(RFC-0009 P-103). Each call decoded and validated the whole staged area - up to 512 units - to
hand outcapof them: 261 ns per unit atcap2. It now examinescap + 1units: 12 ns per
unit atcap2, 2.3 at 64 (from 9.6). The text delivered and the error that ends it are the
same for everycap. -
proven_reader_read_linesearches each byte once (RFC-0009 P-104). It searched from the
start of the pending line, one byte at a time, after every refill, so a long line arriving in
small pieces cost time quadratic in its length: 4.8 us per byte for a 16,000-byte line read a
byte at a time. It now remembers how far it has searched and uses the platformmemchr: 21-25 ns
per byte for that case, and 0.27 ns per byte from 1.33 for 80-byte lines read from a file.
proven_reader_buffered_tgains a field,scanned, set byproven_reader_buffered. -
Listing a directory on POSIX costs one metadata call per entry, or none (RFC-0009 P-102).
proven_fs_dir_next(and soproven_fs_listand the walk) asked for every entry's type twice,
following and not following symlinks.readdir'sd_typealready says what most entries are:
a directory or a special file now needs no call, a regular file one (for its size), and only a
symlink or a filesystem that leavesd_typeunknown takes both. Measured over 50,000 files:
3.5 us per entry, from 5.9. What is reported is unchanged. -
CRC-32 is about 6x faster on long inputs (RFC-0009 P-101): slicing-by-8, eight bytes per
step with independent table lookups. Measured on 1 MiB: 0.36 ns per byte, from 2.30. Output is
unchanged (checked against the single-table path at every length and offset, and against
zlib). It costs 7 KiB more read-only data; a build with-DPROVEN_CRC32_SMALL=1keeps the
single 1 KiB table. -
Integer map keys are hashed with the per-process secret in a default map (RFC-0009 S-001).
They used a public bit-mix finaliser whatever the map was created with, and its inverse is
public, so anyone choosing the keys - user ids, record numbers from a request - could put any
number of them in one bucket and make the map quadratic. A default (proven_map_create)
integer-key map now uses SipHash-1-3 of the key under the same secret as string keys;
proven_map_create_trustedkeeps the finaliser. Measured cost on a default map: about 7 ns
more per lookup on a 1K-key table, 15-20 ns per insert.proven_map_hashvalues for default
integer-key maps change (and differ per process); trusted maps are unchanged. -
Text from the platform is strict, like all other text (RFC-0009 D-003). An environment value
or a directory entry name that is not valid text - bytes that are not UTF-8 on POSIX, a lone
surrogate on Windows - is nowPROVEN_ERR_INVALID_ENCODING. Before, POSIX returned the raw
bytes asPROVEN_OKand Windows silently substituted U+FFFD, which in a listing named a
different file, or none.proven_fs_dir_nextandproven_fs_walk_nextreport such an entry
once (emptyname, other fields filled in) and go on;proven_fs_listrefuses the whole
listing rather than leave a file out. -
The library's source list is one manifest,
build_sources.inc, with a freestanding attribute
per source (RFC-0009 X-005). It was a literal array innob.c, and the hosted-only sources
were listed twice more - for the native freestanding build and for the cross matrix - with
nothing checking the copies agreed. Both now read the manifest; a source-contract test refuses a
hand-kept list innob.c.
Fixed
./nob buildfailed in an unpacked release archive. It runsscripts/project-check.sh,
whose checks read the git repository, and the archive has none - so a build from any release
ZIP stopped at that step. Outside a git checkout the step is now skipped with a
[PROVEN][PROJECT_CHECK][SKIP] reason=not-a-git-checkoutline. The archive also gains
build_sources.inc, whichnob.cnow includes../nob buildcould run a test linked against a library object from the previous build.
A test relinked when a library object was newer than the executable, compared by whole-second
modification time; an object rebuilt in the same second as the previous link compared equal,
and the old binary ran. The objects' contents now go into each test link's state hash (and
into the hash recorded after the link, so a cached build still relinks nothing).- The view-taking macros say how to pass a compound literal (RFC-0009 X-008).
PROVEN_ARG((proven_u8str_view_t){ p, n })and the same inproven_scan_fmt, the print and
append_fmtmacros and thePROVEN_MAP_*_U8_*wrappers do not compile - the preprocessor splits
the literal at its comma - and the error names an internal function. The headers and manual
chapter 1 (EN/KO) now say to pass a variable, aPROVEN_LIT, or the literal in parentheses; a
test pins the parenthesised form. proven_random_set_sourceis no longer a data race when called while other threads draw
(RFC-0009 S-004). The hook was two plain globals, so a late install was undefined behaviour and
a draw could pair one source's function with the other's context. The pair is now published
under a sequence count; a draw always sees a function with its own context. Installing once at
startup is still the advice.- Eight leftover staging files no longer block every later atomic write of a path
(RFC-0009 D-001).proven_fs_write_file_atomicand_durablestaged into the fixed names
<path>.pvtmp00..07and gave up after them, so eight writers killed mid-write - or anyone
who could write the directory - made the path unwritable through the library for good, with
PROVEN_ERR_IO. The suffix is now 13 random characters (64 bits from the entropy source), only
a name collision is retried, any other failure is returned at once with its own code, and
sixteen collisions in a row arePROVEN_ERR_EXISTS. Confidentiality was never affected: the
staging file is created exclusively, so a planted name was refused, not written through. - An environment variable set to the empty string read as
PROVEN_ERR_IOon Windows
(RFC-0009 D-002).GetEnvironmentVariableWreturns 0 both for failure and for "stored 0
characters";GetLastErrornow tells them apart, so the value is an empty string as on POSIX.
A value that grows between the sizing call and the read is re-sized instead of failing. fs.h:proven_fs_copystates its contract, andproven_fs_write_file's[[nodiscard]]
sits on its declaration again (RFC-0009 X-007). The copy's whole documentation was one line;
it now says the copy is not staged (a failure leavesdestpartly written), that symbolic
links are followed at both ends, thatdesttakes the source's permission bits, and that the
read-only rule covers it. The attribute had been separated from its declaration by a long
block comment.