feat(stdlib): STEP 4-A binary I/O — Bytes constructor + LE getters/setters (Refs #239, closes standards#326)#507
Open
hyperpolymath wants to merge 1 commit into
Open
feat(stdlib): STEP 4-A binary I/O — Bytes constructor + LE getters/setters (Refs #239, closes standards#326)#507hyperpolymath wants to merge 1 commit into
hyperpolymath wants to merge 1 commit into
Conversation
…tters (Refs #239, closes #326) Adds the raw binary I/O surface that estate ABI-test ports need (raze-tui's 16-byte RazeEvent record, future tree-sitter-k9 / tree- sitter-a2ml grammar fixtures, half of bofig's contract tests). Stdlib (`stdlib/Deno.affine`): + bytes_new(n) -> new Uint8Array(n) + bytes_fill(n, byte) -> new Uint8Array(n).fill(byte & 0xFF) + bytes_set_u8(b, off, v) -> DataView.setUint8(off, v & 0xFF), 0 + bytes_set_u16_le(b, o, v) -> DataView.setUint16(o, v & 0xFFFF, true), 0 + bytes_set_u32_le(b, o, v) -> DataView.setUint32(o, v >>> 0, true), 0 + bytes_set_i32_le(b, o, v) -> DataView.setInt32(o, v | 0, true), 0 + bytes_get_u8(b, off) -> DataView.getUint8(off) + bytes_get_u16_le(b, off) -> DataView.getUint16(off, true) + bytes_get_u32_le(b, off) -> DataView.getUint32(off, true) + bytes_get_i32_le(b, off) -> DataView.getInt32(off, true) All multi-byte ints are little-endian — the estate's C ABI contracts (raze-tui `raze-events.ads`, Idris2 `Events.idr`) are LE-pinned. Setters return `Int = 0` for expression-statement composition; bounds remain caller's responsibility (out-of-range offsets throw RangeError at the host boundary). Pair with `bytesLength` from STEP 3 / #504 for bounds-checks. Tests: + tests/codegen-deno/bytes_binary_io.{affine,deno.js,harness.mjs} round-trips a raze-tui-shaped RazeEvent (LE i32 + u32 + u8 + u16 × 2) with field-level equality, plus boundary cases (u32 max, i32 -1, byte-order byte-by-byte check via DataView, bytes_fill masking). ✓ All 353 dune-runtest tests pass. ✓ All codegen-Deno-ESM harnesses pass. Out of scope: BE variants (not needed by any estate ABI today); 64-bit getters/setters (defer to `*_i64_le` follow-up if current STEP 4 candidates exceed 32-bit). Refs: hyperpolymath/standards#239 (umbrella), hyperpolymath/standards#326 Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Adds the raw binary I/O surface that estate ABI-test ports need (
raze-tui's 16-byteRazeEventrecord, future tree-sitter-k9 / tree-sitter-a2ml grammar fixtures, half of bofig's contract tests).Companion to the read-only
bytesLength/bytesByteAt/bytesAsciiSliceaccessors shipped in affinescript#504 (STEP 3 / standards#242). Together the two PRs give AffineScript first-class binary-buffer support.What lands
stdlib/Deno.affine(+10 externs)bytes_new(n) -> Bytesnew Uint8Array(n)bytes_fill(n, byte) -> Bytes(new Uint8Array(n)).fill(byte & 0xFF)bytes_set_u8(b, off, v) -> Int… .setUint8(off, v & 0xFF), 0bytes_set_u16_le(b, off, v) -> Int… .setUint16(off, v & 0xFFFF, true), 0bytes_set_u32_le(b, off, v) -> Int… .setUint32(off, v >>> 0, true), 0bytes_set_i32_le(b, off, v) -> Int… .setInt32(off, v | 0, true), 0bytes_get_u8(b, off) -> Int… .getUint8(off)bytes_get_u16_le(b, off) -> Int… .getUint16(off, true)bytes_get_u32_le(b, off) -> Int… .getUint32(off, true)bytes_get_i32_le(b, off) -> Int… .getInt32(off, true)All multi-byte integer variants are little-endian — the estate's C ABI contracts (raze-tui
raze-events.ads, Idris2Events.idr) are LE-pinned. Setters returnInt = 0so they compose in an expression-statement position; the caller is responsible for the buffer-bounds invariant (an out-of-range offset throwsRangeErrorat the host boundary). Bounds-check viabytesLengthfrom #504.Tests
tests/codegen-deno/bytes_binary_io.{affine,deno.js,harness.mjs}— round-trips a raze-tui-shaped RazeEvent record (LE i32 + u32 + u8 + u16 × 2) with field-level equality, plus boundary cases:0xFFFFFFFF) round-tripsDataView(0x12345678writes low byte at offset 4, high byte at offset 7)bytes_fillmasks256 → 0,-1 → 0xFF,0xFF → 0xFFbytes_newproduces a zero-initialised bufferVerification
dune build bin/main.exedune runtest./tools/run_codegen_deno_tests.shOut of scope
*_i64_le) — defer; current STEP 4 candidates max out at 32-bit.Relation to #504 (STEP 3)
Both this PR and #504 add externs to
stdlib/Deno.affineand lowerings tolib/codegen_deno.ml. The two sets are disjoint at the symbol level:statIsFile,statIsDirectory,bytesLength,bytesByteAt,bytesAsciiSlice,importMetaUrl,pub fn ends_with, wildcard-let codegen fix.The bytes test fixture uses uniquely-named
let _r0 = …,let _r4 = …, etc. instead of thelet _ = …form so this PR is independently mergeable from #504; once #504 lands, the fixture could be simplified to use the wildcard pattern.Refs
🤖 Generated with Claude Code