Re-vendor the expanded corpus: 349 vectors through the reader, the writer and the measure - #6
Merged
Merged
Conversation
…iter and the measure STANDARD.md and conformance/ come from mas-bandwidth/serialize@7e0515e, which carries the twenty-six rulings a tenth implementer could not derive, the write side assertion ordering, and the corpus expansion: one discovered file per covered operation, 349 vectors across 18 files, controls, boundary twins, compositional sequences, expect bits, writer = canonical and measure_at_least. ConformanceTests is now the runner STANDARD.md specifies rather than a two operation subset. It discovers conformance/ instead of naming files, refuses to skip a vector whose operation or parameter it cannot drive, and runs one step machine over both the single operation files and the sequence, object and message files, so the two cannot drift apart. Numeric values compare as 128 bit two's complement patterns and never through a float, which is what makes a NaN payload and a quieted signaling bit testable. Every refused vector carries the terminality check by behavior: every later step must refuse, and a further read the vector does not name must fail, consume no bits and write nothing to its own destination. Vectors marked writer = canonical are re-emitted through the write stream and compared byte for byte, flush included, and vectors carrying measure_at_least are held to that floor on the measure stream. 349 vectors from 18 files, 195 writer checks, 9 measure checks, in both the checked and the release shapes. The library gains the object operation. STANDARD.md makes serialize_object composition rather than an encoding, and names it among the operations that must refuse on a stream that has already failed. Java had no spelling for it. BitStream.serializeObject takes a Serializer, contributes no bytes of its own, and consults the read stream's failure latch first, so a nested object on a failed stream refuses without invoking the object. The bits field's width bound now runs at the caller's width. STANDARD.md bounds a bits field by value < 2^bits at every width in [1,64]. serializeBits64 narrows a 64 bit value to a 32 bit group at widths of 32 or fewer, and the only bound left was the one the bit writer applies to the already narrowed group, so 2^32 + 5 in a four bit field arrived as 5 and passed. That is the narrowing class serialize 1.16.1 fixed. The write stream and the measure stream shared the defect and both now call SerializeUtil.valueFitsInBits, which is the one place the rule lives, with a focused test in StreamTests guarding it in the checked shape. The interop job builds the reference conformance runner from the corpus commit rather than from the wire compatibility tag. The two pins answer different questions: SERIALIZE_TAG is a released tag and stays v1.16.0, and a runner older than the corpus cannot drive the operations the corpus carries. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
STANDARD.mdandconformance/come from mas-bandwidth/serialize@7e0515e, byte for byte. 18 files, 349 vectors.What the runner gained
ConformanceTestsdrove two operations,int_relativeandint128, because that was all the vendored corpus held. It is now the runnerSTANDARD.mdspecifies.conformance/*.txtis enumerated at run time. An empty directory fails the run.operationTakesParammirrors the reference.sequence,objectandmessagefiles. A single operation vector is a one or two step sequence built from the record's own parameters,preceding_bitsbecoming a leadingbitsstep, so the sequence files cannot drift away from the operation files.float,doubleandcompressed_floatbit patterns compare as 128 bit two's complement patterns, so a hexadecimal expectation and its decimal twin are one expectation, NaN compares unequal to itself, and a quieted signaling bit is visible. Java has no unsigned integers and no 128 bit type, so the vector numbers travel asBigIntegerand land in the operation's own width at the call site.writer = canonicalis re-emitted through the write stream and the whole emitted stream is compared byte for byte, flush included, which is where the trailing bits obligation bites.measure_at_leastruns through the measure stream as a floor.0xA5, the empty stream of a zero bit read included.#begins a comment at the start of a line and nowhere else. Numbers are signed decimal or0xhexadecimal, parsed to 128 bits.Identical counts to the reference runner, in both
make test(assertions on) andmake test-release(assertions off). No corpus vector fires an assertion under-ea: a refusal is a refusal in both shapes.What the rulings implied for the library
objectis a required operation and Java had no spelling for it.STANDARD.mdmakesserialize_objectcomposition rather than an encoding, and names it among the operations that must refuse on a stream that has already failed.BitStream.serializeObject( Serializer )runs a nested object inline, contributes no bytes of its own, and on a read stream consults the failure latch before it invokes the object.object.txt's twins are what hold it: the same operations nested and flat must produce the same bytes and the same consumption.The
bitswidth bound now runs at the caller's width. This is the narrowing class of serialize 1.16.1, and it is present in this port.STANDARD.md: the boundvalue < 2^bitsholds at every width in[1,64].serializeBits64narrows a 64 bit value to a 32 bit group at widths of 32 or fewer:The only bound left was the one
BitWriter.checkWriteBitsapplies to the already narrowed group, so a value of2^32 + 5in a four bit field arrived as5and passed. Above 32 bits the high group's own check happens to see the top bits, so the defect is exactly the widths at or below 32.SerializeUtil.valueFitsInBits( long value, int bits )is now the one place the rule lives, called by bothWriteStream.serializeBits64andMeasureStream.serializeBits64before any narrowing, mirroringserialize::value_fits_in_bits. The measure stream had no value bound at all.What else was grepped and found clean.
serializeIntRelativetakesint, which is the operation's own API type, so a wider caller value narrows visibly at the call site rather than inside the library.serializeStringandserializeWideStringcompareutf8.lengthandString.length()againstbufferSize, allintalready, so there is no narrowing between the length and its check.serializeFixedassertsLong.compareUnsigned( offset, rawRange )on the 64 bit offset before writing the 32 bit groups.serializeCompressedFloatnarrowsmaxIntegerValuetointonly to hand it tobitsRequired, which reads it unsigned, and the value is bounded below2^32by the normative clamp.Rulings already satisfied, confirmed against the corpus. No trap or abort on malformed input, Java arithmetic wraps. Every read consults the failure state first, degenerate ranged reads, a zero count
bytes, an already alignedalignand a degeneratefixedincluded. The offset form of the range check at every width,Integer.compareUnsigned,Long.compareUnsignedandUInt128Value.compareUnsigned. Invalid UTF-8 as Unicode Table 3-7.wstringgroups above0xFFFF, unpaired surrogates and zero groups refused, pairs accepted, no alignment anywhere.compressed_floatin float32 with two roundings and the normative integer clamp. The measure charging 7 bits per alignment performing operation.Negative controls
The corpus is an instrument, and an instrument nobody has seen go red is a claim rather than a measurement. Each sabotage was reverted.
1. Unknown operation
conformance-controls/unknown-operation.txtcopied intoconformance/, run, then deleted. It is not committed.2. The alignment padding zero check deleted
BitReader.readAlignreduced to consuming the padding and always returning true. Ten vectors across five files go red.3.
mindropped from the ranged int reconstructionReadStream.serializeInt'svalue.value = unsignedValue + minreduced tovalue.value = unsignedValue. Six vectors go red, exactly the ones with a non-zeromin. The degenerate range vectors take the early return and the[0,N]ranges are unaffected, which is the right shape.4. The narrowing fix sabotaged back
checkBits64returned to the pre-fix form, taking only the width, so the sole remaining bound is the one applied to the already narrowed group.Reverted, the same run is
458 tests, 4196 checks, 0 failed.CI
SERIALIZE_TAGis untouched atv1.16.0. Theinteropjob now builds the reference conformance runner from a second pin,SERIALIZE_CORPUS_COMMIT, the commit this repository vendorsSTANDARD.mdandconformance/from. The two pins answer different questions: the wire compatibility pin is a released tag and must stay one, and a runner older than the corpus cannot drive the operations the corpus carries, so the runner travels with the vectors.Version
SerializeUtil.VERSIONand the README line move to 1.1.2. The README line naming the current published release tag is untouched. No tag.🤖 Generated with Claude Code