Skip to content

feat(stdlib): STEP 4-A binary I/O — Bytes constructor + LE getters/setters (Refs #239, closes standards#326) - #507

Merged
hyperpolymath merged 3 commits into
mainfrom
claude/step4-a-binary-io
Jun 1, 2026
Merged

feat(stdlib): STEP 4-A binary I/O — Bytes constructor + LE getters/setters (Refs #239, closes standards#326)#507
hyperpolymath merged 3 commits into
mainfrom
claude/step4-a-binary-io

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

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).

Companion to the read-only bytesLength / bytesByteAt / bytesAsciiSlice accessors 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)

extern lowers to notes
bytes_new(n) -> Bytes new Uint8Array(n) zeroed
bytes_fill(n, byte) -> Bytes (new Uint8Array(n)).fill(byte & 0xFF) all-byte buffer
bytes_set_u8(b, off, v) -> Int … .setUint8(off, v & 0xFF), 0 returns 0
bytes_set_u16_le(b, off, v) -> Int … .setUint16(off, v & 0xFFFF, true), 0 LE
bytes_set_u32_le(b, off, v) -> Int … .setUint32(off, v >>> 0, true), 0 LE
bytes_set_i32_le(b, off, v) -> Int … .setInt32(off, v | 0, true), 0 LE
bytes_get_u8(b, off) -> Int … .getUint8(off)
bytes_get_u16_le(b, off) -> Int … .getUint16(off, true) LE
bytes_get_u32_le(b, off) -> Int … .getUint32(off, true) LE
bytes_get_i32_le(b, off) -> Int … .getInt32(off, true) LE

All multi-byte integer variants are little-endian — the estate's C ABI contracts (raze-tui raze-events.ads, Idris2 Events.idr) are LE-pinned. Setters return Int = 0 so they compose in an expression-statement position; the caller is responsible for the buffer-bounds invariant (an out-of-range offset throws RangeError at the host boundary). Bounds-check via bytesLength from #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:

  • u32 max (0xFFFFFFFF) round-trips
  • i32 -1 preserves sign
  • LE byte order verified byte-by-byte via DataView (0x12345678 writes low byte at offset 4, high byte at offset 7)
  • bytes_fill masks 256 → 0, -1 → 0xFF, 0xFF → 0xFF
  • bytes_new produces a zero-initialised buffer

Verification

step result
dune build bin/main.exe
dune runtest ✅ 353/353
./tools/run_codegen_deno_tests.sh ✅ all harnesses (incl. the new one)

Out of scope

  • Big-endian variants — not needed by any current estate ABI; revisit if/when an external API forces BE.
  • 64-bit getters/setters (*_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.affine and lowerings to lib/codegen_deno.ml. The two sets are disjoint at the symbol level:

The bytes test fixture uses uniquely-named let _r0 = …, let _r4 = …, etc. instead of the let _ = … 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

…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>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 83 issues detected

Severity Count
🔴 Critical 2
🟠 High 13
🟡 Medium 68

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action ons/checkout@v6\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action land/setup-deno@v2\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 87 issues detected

Severity Count
🔴 Critical 2
🟠 High 16
🟡 Medium 69

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action ons/checkout@v6\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action land/setup-deno@v2\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 87 issues detected

Severity Count
🔴 Critical 2
🟠 High 16
🟡 Medium 69

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action ons/checkout@v6\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action land/setup-deno@v2\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath disabled auto-merge June 1, 2026 01:38
@hyperpolymath
hyperpolymath merged commit 333a0df into main Jun 1, 2026
26 of 27 checks passed
@hyperpolymath
hyperpolymath deleted the claude/step4-a-binary-io branch June 1, 2026 01:39
hyperpolymath added a commit that referenced this pull request Jun 1, 2026
…ses standards#327) (#509)

Adds the randomness + high-res-timer surface that estate property tests
and benchmark fixtures need:
- `bofig/tests/property/graph_properties_test.ts` (377L) substitutes
`\`\${prefix}_\${Math.floor(Math.random() * 1000000)}\`` for ID
generation.
- Bench tests want sub-millisecond timings (`performance.now()`).

## What lands

### `stdlib/Deno.affine` (+4 externs)

| extern | lowers to | notes |
|---|---|---|
| `math_random() -> Float` | `Math.random()` | `[0, 1)`. JS PRNG,
non-crypto. |
| `random_u32() -> Int` | `((Math.random() * 4294967296) >>> 0)` |
uniform u32 |
| `random_in_range(lo, hi) -> Int` | `Math.floor(Math.random() * (hi -
lo)) + lo` | uniform `[lo, hi)` |
| `performance_now() -> Float` | `performance.now()` | high-res sub-ms
timer |

`math_random` is the JS PRNG — **not cryptographically secure**.
Sufficient for property-test input generation, sampling, and
simulations. For cryptographic random bytes a separate
`crypto_random_bytes` binding routing to `crypto.getRandomValues()`
belongs in a different sub-issue (different host call, different threat
model).

### Tests

`tests/codegen-deno/random_smoke.{affine,deno.js,harness.mjs}` covers:
- 1000 `math_random` draws all in `[0, 1)`
- 10000 `random_u32` draws all in `[0, 2^32)` with ≥ 1000 distinct
values (catches degenerate-PRNG regression)
- 1000 `random_in_range(0, 100)` draws all in `[0, 100)`
- 500 `random_in_range(50, 60)` draws cover most of the window
- `performance_now` monotone non-decreasing across three consecutive
calls (catches clock-resolution regression)

## Verification

| step | result |
|---|---|
| `dune build bin/main.exe` | ✅ |
| `dune runtest` | ✅ 353/353 |
| `./tools/run_codegen_deno_tests.sh` | ✅ all harnesses (incl. the new
one) |

## Out of scope

- **Seeded RNG** (`@std/random`) — defer until a determinism-needing
property test surfaces.
- **`sleep` / `setTimeout`** — separate effect surface; not blocking
STEP 4.

## Relation to #504 + #507

The three STEP 3 / STEP 4-A / STEP 4-B PRs all add disjoint externs to
`stdlib/Deno.affine` and `lib/codegen_deno.ml`. Each is independently
mergeable from origin/main; expected merge order is #504#507 → this
PR but the file sections are separate enough that the rebases are
mechanical.

## Refs

- Closes standards#327
- Refs: standards#239 (umbrella), standards#243 (STEP 4 per-repo unblock
target), affinescript#504 (STEP 3 sibling), affinescript#507 (STEP 4-A
sibling)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant