Skip to content

Releases: mas-bandwidth/serialize.rs

serialize.rs 3.1.0: object read terminality

Choose a tag to compare

@gafferongames gafferongames released this 27 Oct 11:25
8e4f4d1

Format 2.0, unchanged. Corpus, standard and controls re-synced with reference mas-bandwidth/serialize v2.0.1. New: Stream::serialize_object, which refuses a nested object on a failed read stream on entry and exit (the reference's serialize_object). Adds the object-terminal control. C++ interop pinned to v2.0.1.

serialize 3.0.0: format 2.0

Choose a tag to compare

@gafferongames gafferongames released this 22 Oct 17:50
fb9cb71

Implements serialize format 2.0 (mas-bandwidth/serialize v2.0.0). Breaking: finite floats only (NaN and infinity refused), wstring removed, packet readers call serialize_end (trailing data and non-zero padding refused), Error is the closed list of eight refusal reasons (Overflow is now PastEnd, Align NonzeroPadding, InvalidString InvalidUtf8 or EmbeddedNul), int_relative refuses its non-canonical absolute tier, the string NUL check runs before UTF-8 validation. Conformance 397/397 with every refusal's reason checked; all 15 reference controls fail exactly their expected lists. Byte-identical interop with the C++ reference v2.0.0.

The 351-vector corpus, and refuse_if_failed

Choose a tag to compare

@rowan-claude rowan-claude released this 16 Oct 09:45
5e26a78

The conformance runner discovers and runs the full shared corpus, 351 vectors across every type, through the read, write and measure paths, and refuses on any failed vector rather than skipping it (#70, #71, #72). refuse_if_failed is now public API: a defaulted Stream method, so an outside implementation can halt a stream on the first failed read (#71, #72). A zero-bit read is carried through the failure gate like any other width (#71).

The live interop job pins the C++ reference at v1.16.2 and exchanges bytes both ways (#68, #72).

serialize-official 2.3.0: format version 1.1

Choose a tag to compare

@gafferongames gafferongames released this 04 Sep 07:55
af60284

This release brings the Rust runtime to format version 1.1 of the wire standard. int_relative reconstructs every tier in a width that cannot wrap and refuses a result outside 0 to 2^31 - 1 or not strictly above previous; the absolute tier reads its 32 raw bits unsigned and refuses a set top bit; a refused read writes nothing to a scalar destination, which the documentation always promised and the absolute tier did not keep; the read stream now latches failure (ReadStream::failure) until it is re-initialized, at the cost of one branch folded into the existing past-end check; a ranged int128 with min equal to max is legal and costs zero bits. The standard's conformance corpus is vendored and every vector runs in the test suite, and the vendored STANDARD.md and corpus are checked against upstream in CI. Stream::refuse is the new latching refusal for implementors of the trait; Stream::fail stays as a plain error constructor and does not latch. The wire is unchanged: 126 bytes byte-identical against the C++ implementation both ways.

On crates.io as serialize-official

Choose a tag to compare

@rowan-claude rowan-claude released this 26 Aug 04:19
6b52aa6

The crate publishes on crates.io as serialize-official — the bare name serialize is reserved by the registry. In code the library is still use serialize::*; the [lib] name is unchanged.

Identical to v2.2.1 in every wire byte. The version exists so the tag, the manifest and the published crate agree.

v2.2.1 — tag, manifest and crate agree, and the clamp is cross-language-proven

Choose a tag to compare

@rowan-claude rowan-claude released this 26 Aug 02:50
87f1431

Cargo.toml and Cargo.lock carry 2.2.1, so the tag, the manifest and the crate say the same version. The compressed-float range clamp carries cross-language witnesses: the wire-compatibility gate pins the C++ reference at v1.14.0 and proves the clamped values byte-identical across the boundary. STANDARD.md is re-synced with upstream, including the corrected contraction-suppression guidance and the writer-fusion vector band.

v2.2.0 — precomputed compressed-float entry points, the normative range clamp, and string benches

Choose a tag to compare

@rowan-claude rowan-claude released this 23 Aug 06:25
9648026

Three things for users of this crate, all merged on green this week.

Precomputed compressed-float entry points — serialize_compressed_float_precomputed takes the four derived constants (step count, wire width, delta, min) computed once at build or generation time, skipping the per-call derivation. Additive: the existing API is untouched, and the two paths are wire-identical by construction, held by the differential suite.

The normative integer clamp (STANDARD.md, 2026-08-23). For declarations with step counts in [2^23, 2^24), float32 rounding could push the quantized code past max_integer_value — a top-of-range write was rejected by its own reader, or leaked a 25th bit into the stream. The writer clamps after the floor; no byte changes outside that band. Witnesses pinned in the tests and corpus.

string and wstring bench rows — first in this crate, with baselines recorded.

🤖 Generated with Claude Code

API misuse checks compile out of release builds: the Rust check model matches the C++ reference

Choose a tag to compare

@rowan-claude rowan-claude released this 19 Aug 07:23

No wire change. Wire bytes are identical to 2.1.1, and the semver gate confirms the API is compatible.

A release build of this crate now performs the same amount of API-misuse checking the C++ library does: none. The eight hard assert! sites, plus the shared misuse macro's hard read leg, are debug_assert! (#47).

  • The read path's remaining 1.x hard panics were on invalid arguments, not on wire data: bit widths outside [1,32], min > max, buffer_size below 2, and BitReader::new given bytes beyond the buffer. Those were this port's invention. The C++ library compiles every serialize_assert out in release, read side and write side alike, so they compile out here too. Found by the 2026-08-16 six-language check-model audit (issue #45), against the family standard: "the caller is responsible for well formed writes... We want MINIMAL runtime checking in release."
  • This also closes a doc/code contradiction: the crate docs claimed the write-spine width assert was debug-only while BitWriter::new and write_bits made it hard.
  • Unchanged in every build, because these validate the wire rather than the caller: buffer-end Error::Overflow, Error::Align, Error::ValueOutOfRange, and the string and wstring content refusals. Safe Rust's own slice bounds checks also stay, the named language cost of unsafe_code = "forbid".
  • Debug builds are unaffected. Every moved check still fires under debug_assert!.

The changelog now names what shipped when

CHANGELOG.md's single Unreleased block had grown to span three tags, so anyone cutting notes from it would have re-announced shipped work. It is split at the tags it actually covers: 2.1.2, 2.1.1, and 2.1.0, whose UTF-16 code unit wstring move and reader refusals shipped on 2026-08-15 while the heading still read Unreleased.

GitHub release only: crates.io publishing is a separate, deliberate gate, unchanged.

Full changelog: v2.1.1...v2.1.2

The emoji-first wstring vector is byte-pinned, and the standard carries the Implementation Law

Choose a tag to compare

@rowan-claude rowan-claude released this 17 Aug 09:53
e82eecd

The family's exact-vector conformance pin holds in Rust: the emoji-first wstring vector — U+1F600 followed by U+0041 through an 8-unit buffer — encodes to the identical bytes on every implementation, asserted byte-for-byte (#43).

  • The emoji-first family vector is pinned as exact bytes: the astral-pair-then-BMP ordering exercises the surrogate split at the front of the buffer, and the test refuses any drift from the family's wire (#43).
  • STANDARD.md mirrors upstream verbatim at the Implementation Law revision — the law governs implementation practice, not the wire; no code change is implied (#46).

GitHub release only: crates.io publishing is a separate, deliberate gate.

wstring rides as UTF-16 code units, and the family's wire is one wire

Choose a tag to compare

@rowan-claude rowan-claude released this 15 Aug 22:16
8d860ac

Wire bytes move for astral text, stated first: a char above 0xFFFF now rides as its surrogate pair (two 32-bit groups), and the length field counts units — the old single code-point group, which the rest of the released family refuses, is gone from the write path and refused on read. BMP text is byte-identical to 2.0.0. This is the same intentional family break shipped in serialize v1.9.0, serialize.c v1.3.0 and serialize.cs v1.3.0; with this release all five implementations speak one wstring wire, and astral cross-language interop is certifiable at the family's interop pins.

  • writer: char::encode_utf16 is the split (std's crystal, not hand arithmetic); measure agrees with write; infallible-writes shape (2.0.0) preserved.
  • reader: refuses groups above 0xFFFF, interior NUL groups, unpaired and misordered surrogates, and a dangling high as the final group; well-formed pairs recombine into the char. Rust strings cannot hold unpaired surrogates, so refusal is the only conforming behavior.
  • proofs: the family conformance pin ("a" + U+1F600, 8-unit buffer) byte-compared against serialize's pinned sequence; BMP golden unchanged; doctored refusal vectors proven both ways with accept-boundary neighbors; #![forbid(unsafe_code)] intact.

Not published to crates.io — the standing gate is unchanged.