cultcache-rs docs: state the decoder's fixed stack floor, record the Windows run

The README and decode_payload claimed the calling thread needs only its own
frames. Measured, the door has a payload-independent floor (release 56 KiB vs
16 for a same-signature non-decoding function; debug 104 vs 88). Say so, and
replace "Windows not yet verified" with the one by-hand Starfire run of
decode_stack on x86_64-pc-windows-msvc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-14 20:57:58 +02:00
commit dbafbf52bc
2 changed files with 10 additions and 5 deletions

View file

@ -470,8 +470,11 @@ The guarantee holds for records the derives produce. `MAX_RECORD_INLINE_BYTES`
record may have, and the derives refuse a larger one at compile time: put its
large data behind a `Vec` or a `Box`. A field of a type the derives did not
produce that is larger than that, behind a `Box` or `Vec`, is outside the
guarantee. The caller's thread needs only its own frames, which hold the
returned value. Stack growth on Windows is not yet verified. The crate does not
guarantee. The decoder's own frames have a fixed floor that no payload changes: at the bound a thread decodes a record of
this size from about 56 KiB in a release build and 104 KiB in a debug build, against 16 and 88 KiB for a function
of the same signature that decodes nothing, plus the caller's own frames. Stack growth was verified on
x86_64-pc-windows-msvc once, by hand, on Starfire (`decode_stack`, debug and release); later `stacker` bumps repeat it.
The crate does not
build for wasm32 (`fs2`), and no wasm behaviour is claimed.
### Build dependencies

View file

@ -704,9 +704,11 @@ fn decode_grown<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> std::result::Re
/// rmp-serde depth counter (1024) is only a backstop. A failure keeps its kind and never quotes the
/// bytes. Readers of stored envelope payloads outside this crate decode through it too.
///
/// A payload inside the bound decodes whatever the calling thread's stack: the thread needs only its
/// own frames that hold the returned value. That holds for every record the derives produce, which
/// [`MAX_RECORD_INLINE_BYTES`] bounds; Windows stack growth is not yet verified.
/// A payload inside the bound decodes on any thread that has the decoder's fixed floor, which no payload
/// changes: at the bound, about 56 KiB in release and 104 KiB in debug, against 16 and 88 KiB for a
/// same-signature function that decodes nothing, plus the caller's own frames. That holds for every
/// record the derives produce, which [`MAX_RECORD_INLINE_BYTES`] bounds. Stack growth was verified on
/// Windows once, by hand; a `stacker` bump repeats it.
pub fn decode_payload<T: serde::de::DeserializeOwned>(payload: &[u8]) -> Result<T> {
check_payload_depth(payload)?;
decode_grown(payload).map_err(opaque_decode_error)