tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

commit 6bfbc13b4d5e8a86403a1fbbff3b5384b9a53ace
parent 1dc3277a6c4286801efbcf0927b527b96fb0b961
Author: James Eversole <james@eversole.co>
Date:   Fri,  8 May 2026 09:12:20 -0500

Arborix -> Arboricx rename

Diffstat:
MAGENTS.md | 26+++++++++++++-------------
Adocs/arboricx-bundle-cbor-v1.md | 339+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddocs/arborix-bundle-cbor-v1.md | 339-------------------------------------------------------------------------------
Mext/js/package.json | 8++++----
Mext/js/src/bundle.js | 8++++----
Mext/js/src/cbor.js | 2+-
Mext/js/src/cli.js | 4++--
Mext/js/src/manifest.js | 8++++----
Mext/js/src/merkle.js | 4++--
Mext/js/test/bundle.test.js | 32++++++++++++++++----------------
Mext/js/test/merkle.test.js | 42+++++++++++++++++++++---------------------
Mext/js/test/run-bundle.test.js | 22+++++++++++-----------
Mflake.nix | 3---
Alib/arboricx.tri | 654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dlib/arborix.tri | 654-------------------------------------------------------------------------------
Msrc/FileEval.hs | 2+-
Msrc/Main.hs | 8++++----
Msrc/Research.hs | 6+++---
Msrc/Server.hs | 12++++++------
Msrc/Wire.hs | 22+++++++++++-----------
Mtest/Spec.hs | 332+++++++++++++++++++++++++++++++++++++++----------------------------------------
Atest/fixtures/false.arboricx | 0
Dtest/fixtures/false.arborix | 0
Atest/fixtures/id.arboricx | 0
Dtest/fixtures/id.arborix | 0
Atest/fixtures/map.arboricx | 0
Dtest/fixtures/map.arborix | 0
Atest/fixtures/notQ.arboricx | 0
Dtest/fixtures/notQ.arborix | 0
Atest/fixtures/true.arboricx | 0
Dtest/fixtures/true.arborix | 0
31 files changed, 1259 insertions(+), 1268 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -40,7 +40,7 @@ nix build .# | `REPL.hs` | Interactive Read-Eval-Print Loop (haskeline) | | `Research.hs` | Core types, `apply` reduction, booleans, marshalling (`ofString`, `ofNumber`), output formatters (`toAscii`, `toTernaryString`, `decodeResult`) | | `ContentStore.hs` | SQLite-backed term persistence | -| `Wire.hs` | Arborix portable wire format — encode/decode/import/export of Merkle-DAG bundle blobs | +| `Wire.hs` | Arboricx portable wire format — encode/decode/import/export of Merkle-DAG bundle blobs | ### File extensions @@ -123,14 +123,14 @@ NLeaf → 0x00 NStem(h) → 0x01 || h (32 bytes) NFork(l,r) → 0x02 || l (32 bytes) || r (32 bytes) -hash = SHA256("arborix.merkle.node.v1" <> 0x00 <> serialized_node) +hash = SHA256("arboricx.merkle.node.v1" <> 0x00 <> serialized_node) ``` This is stored in SQLite via `ContentStore.hs`. Hash suffixes on identifiers (e.g., `foo_abc123...`) are validated: 16–64 hex characters (SHA256). -## 7. Arborix Portable Wire Format +## 7. Arboricx Portable Wire Format -The **Arborix wire format** (module `Wire.hs`) defines a portable binary bundle for exchanging Tree Calculus terms, their Merkle DAGs, and associated metadata. It is versioned and schema-driven. +The **Arboricx wire format** (module `Wire.hs`) defines a portable binary bundle for exchanging Tree Calculus terms, their Merkle DAGs, and associated metadata. It is versioned and schema-driven. ### Header @@ -143,7 +143,7 @@ The **Arborix wire format** (module `Wire.hs`) defines a portable binary bundle +------------------+-----------------+------------------+ ``` -- **Magic**: `ARBORIX\0` (`0x41 0x52 0x42 0x4f 0x52 0x49 0x58 0x00`) +- **Magic**: `ARBORICX` (`0x41 0x52 0x42 0x4f 0x52 0x49 0x43 0x58`) - **Header length**: 32 bytes - **Major version**: `1` | **Minor version**: `0` @@ -172,18 +172,18 @@ The manifest describes the bundle's semantics, exports, and schema. Key fields: | Field | Value | Description | |-------|-------|-------------| -| `schema` | `"arborix.bundle.manifest.v1"` | Manifest schema version | +| `schema` | `"arboricx.bundle.manifest.v1"` | Manifest schema version | | `bundleType` | `"tree-calculus-executable-object"` | Bundle category | | `tree.calculus` | `"tree-calculus.v1"` | Tree calculus version | | `tree.nodeHash.algorithm` | `"sha256"` | Hash algorithm | -| `tree.nodeHash.domain` | `"arborix.merkle.node.v1"` | Hash domain string | -| `tree.nodePayload` | `"arborix.merkle.payload.v1"` | Payload encoding | +| `tree.nodeHash.domain` | `"arboricx.merkle.node.v1"` | Hash domain string | +| `tree.nodePayload` | `"arboricx.merkle.payload.v1"` | Payload encoding | | `runtime.semantics` | `"tree-calculus.v1"` | Evaluation semantics | -| `runtime.abi` | `"arborix.abi.tree.v1"` | Runtime ABI | +| `runtime.abi` | `"arboricx.abi.tree.v1"` | Runtime ABI | | `closure` | `"complete"` | Bundle must be a complete DAG | | `roots` | `[{"hash": "...", "role": "..."}]` | Named root hashes | | `exports` | `[{"name": "...", "root": "..."}]` | Export aliases for roots | -| `metadata.createdBy` | `"arborix"` | Originator | +| `metadata.createdBy` | `"arboricx"` | Originator | ### Section 2 — Nodes (Binary) @@ -252,7 +252,7 @@ tricu/ │ ├── REPL.hs │ ├── Research.hs │ ├── ContentStore.hs -│ └── Wire.hs # Arborix portable wire format +│ └── Wire.hs # Arboricx portable wire format ├── test/ │ ├── Spec.hs # Tasty + HUnit tests │ ├── *.tri # tricu test programs @@ -270,9 +270,9 @@ tricu/ └── AGENTS.md # This file ``` -## 9. JS Arborix Runtime +## 9. JS Arboricx Runtime -A JavaScript implementation of the Arborix portable bundle runtime lives in `ext/js/`. +A JavaScript implementation of the Arboricx portable bundle runtime lives in `ext/js/`. It is a reference implementation — not a tricu source parser. It reads `.tri.bundle` files produced by the Haskell toolchain, verifies Merkle node hashes, reconstructs tree values, and reduces them. From project root: diff --git a/docs/arboricx-bundle-cbor-v1.md b/docs/arboricx-bundle-cbor-v1.md @@ -0,0 +1,339 @@ +# Arboricx Portable Bundle v1 (CBOR Manifest Profile) + +Status: **Draft, implementation-aligned** (derived from `src/Wire.hs` as of 2026-05-07) + +This document specifies the **actual on-wire format and validation behavior** currently implemented by `tricu` for Arboricx bundles, with a focus on the newer CBOR manifest path. + +--- + +## 1. Scope + +This profile defines: + +1. The binary container envelope (header + section directory + section payloads). +2. The CBOR manifest section format. +3. The Merkle node section format. +4. Decode/verify/import behavior in `Wire.hs`. +5. Known gaps and sane resolutions. + +Non-goals: + +- tricu source parsing/lambda elimination/module semantics. +- Signature systems / trust policy. +- Compression codecs beyond `none`. + +--- + +## 2. Container format + +A bundle is a byte stream: + +``` +[32-byte header] +[section directory: section_count * 60 bytes] +[section payload bytes...] +``` + +### 2.1 Header (32 bytes) + +| Field | Size | Encoding | Value / Notes | +|---|---:|---|---| +| Magic | 8 | raw bytes | `41 52 42 4f 52 49 58 00` (`"ARBORICX"`) | +| Major | 2 | u16 BE | Must be `1` | +| Minor | 2 | u16 BE | Currently `0` | +| SectionCount | 4 | u32 BE | Number of section directory entries | +| Flags | 8 | u64 BE | Currently emitted as `0`; not interpreted | +| DirectoryOffset | 8 | u64 BE | Offset of section directory (currently `32`) | + +Reader behavior: +- Reject if total bytes < 32. +- Reject bad magic. +- Reject major != 1. + +### 2.2 Section directory entry (60 bytes each) + +| Field | Size | Encoding | Notes | +|---|---:|---|---| +| Type | 4 | u32 BE | e.g. 1=manifest, 2=nodes | +| Version | 2 | u16 BE | Currently emitted as `1`; not enforced on read | +| Flags | 2 | u16 BE | bit0 = critical | +| Compression | 2 | u16 BE | `0` = none (required) | +| DigestAlgorithm | 2 | u16 BE | `1` = SHA-256 (required) | +| Offset | 8 | u64 BE | Absolute byte offset | +| Length | 8 | u64 BE | Section payload length | +| Digest | 32 | raw bytes | SHA-256 of section bytes | + +Reader behavior: +- Reject unknown **critical** section types. +- Reject compression != 0. +- Reject digest algorithm != 1. +- Reject out-of-bounds sections. +- Reject digest mismatch. + +### 2.3 Required section types + +| Type | Name | Required | +|---:|---|---| +| 1 | manifest | yes | +| 2 | nodes | yes | + +Decode currently rejects duplicate section type 1 or 2. + +--- + +## 3. Manifest section (CBOR) + +Manifest bytes are CBOR-encoded map data (using `cborg`). + +### 3.1 Top-level manifest schema + +Top-level map has **exactly 8 keys** in this exact decode order in current implementation: + +1. `schema` (text) +2. `bundleType` (text) +3. `tree` (map) +4. `runtime` (map) +5. `closure` (text: `"complete"|"partial"`) +6. `roots` (array) +7. `exports` (array) +8. `metadata` (map) + +> Important: Current decoder is order-strict; it expects keys in this sequence. + +### 3.2 Nested structures + +#### `tree` map (3 keys, order-strict) +- `calculus`: text +- `nodeHash`: map +- `nodePayload`: text + +`nodeHash` map (2 keys, order-strict): +- `algorithm`: text +- `domain`: text + +#### `runtime` map (4 keys, order-strict) +- `semantics`: text +- `evaluation`: text +- `abi`: text +- `capabilities`: array(text) + +#### `roots` array of maps +Each root map has 2 keys (order-strict): +- `hash`: bytes (raw 32-byte hash payload encoded as CBOR byte string) +- `role`: text + +#### `exports` array of maps +Each export map has 4 keys (order-strict): +- `name`: text +- `root`: bytes (32-byte hash) +- `kind`: text +- `abi`: text + +#### `metadata` map +Flexible key set; decoded as map(text -> text), then projected into optional fields: +- `package` +- `version` +- `description` +- `license` +- `createdBy` + +Unknown metadata keys are ignored. + +### 3.3 Default emitted manifest values + +Writers in `Wire.hs` currently emit: + +- `schema = "arboricx.bundle.manifest.v1"` +- `bundleType = "tree-calculus-executable-object"` +- `tree.calculus = "tree-calculus.v1"` +- `tree.nodeHash.algorithm = "sha256"` +- `tree.nodeHash.domain = "arboricx.merkle.node.v1"` +- `tree.nodePayload = "arboricx.merkle.payload.v1"` +- `runtime.semantics = "tree-calculus.v1"` +- `runtime.evaluation = "normal-order"` +- `runtime.abi = "arboricx.abi.tree.v1"` +- `runtime.capabilities = []` +- `closure = "complete"` +- `metadata.createdBy = "arboricx"` + +--- + +## 4. Nodes section (binary) + +Node section payload layout: + +``` +node_count: u64 BE +repeat node_count times: + hash: 32 bytes + payload_len: u32 BE + payload: payload_len bytes +``` + +Node payload grammar: + +- `0x00` => Leaf +- `0x01 || child_hash(32)` => Stem +- `0x02 || left_hash(32)||right(32)` => Fork + +Section decoder rejects: +- duplicate node hashes, +- truncated entries, +- payload overruns, +- trailing bytes after final node. + +--- + +## 5. Verification behavior (`verifyBundle`) + +`verifyBundle` enforces all of: + +1. bundle version >= 1. +2. bundle has at least one node. +3. manifest constants match hardcoded v1 values (schema/type/calculus/hash algo/domain/payload/runtime semantics/ABI). +4. runtime capabilities must be empty. +5. closure must be `complete`. +6. manifest has at least one root and one export. +7. root sets in `bundleRoots` and `manifest.roots` must match exactly. +8. each root and export root exists in node map. +9. each node payload deserializes and re-hashes to declared node hash. +10. all referenced child hashes exist. +11. full closure reachability from roots succeeds. + +`importBundle` runs decode + verify before storing nodes. + +--- + +## 6. Export/import semantics + +### 6.1 Export + +`exportNamedBundle`: +- Traverses reachable nodes for each requested root hash. +- Builds node map. +- Builds default manifest and CBOR bytes. +- Emits two sections (manifest, nodes). + +`exportBundle` auto-names exports: +- 1 root => `root` +- N>1 => `root0`, `root1`, ... + +### 6.2 Import + +`importBundle`: +1. Decode bundle. +2. Verify bundle. +3. Insert all node payloads into content store. +4. For each manifest export: reconstruct tree by export root and store name binding in DB. +5. Return bundle root list. + +--- + +## 7. Determinism properties + +Current implementation is deterministic for identical logical input because: +- Node map serialized in ascending hash order (`Map.toAscList`). +- Field order in manifest encoding is fixed by code. +- Section ordering is fixed: manifest then nodes. + +So repeated exports of same roots produce byte-identical bundles. + +--- + +## 8. Known gaps and sane resolutions + +These are important design gaps visible from current code. + +### Gap A: Node hash domain mismatch risk (critical) + +Status: **resolved in current codebase**. + +What was wrong: +- Manifest declared `tree.nodeHash.domain = "arboricx.merkle.node.v1"`. +- Hashing implementation previously used `"tricu.merkle.node.v1"`. + +Current state: +- Haskell hashing now uses `"arboricx.merkle.node.v1"`. +- JS reference runtime hashing now uses `"arboricx.merkle.node.v1"`. +- JS manifest validation now requires `"arboricx.merkle.node.v1"`. + +Remaining recommendation: +- Keep hash-domain constants centralized/shared to prevent future drift. +- Add explicit test vectors for Leaf/Stem/Fork hashes under the Arboricx domain. + +### Gap B: CBOR decode is order-strict, not generic-map tolerant + +Observed: +- Decoder expects exact key order for most maps. + +Impact: +- Another canonical CBOR writer that reorders keys may decode-fail even if semantically equivalent. + +Sane resolution: +- For v1 compatibility, decode maps as unordered key/value collections, require key presence and types, and reject unknown keys only where desired. +- Keep writer deterministic, but relax reader. + +### Gap C: “Canonical CBOR” claim is stronger than implementation + +Observed: +- Writer uses fixed order but does not explicitly sort keys per RFC 8949 canonical ordering rules. + +Sane resolution: +- Either (a) rename as “deterministic CBOR” profile, or (b) implement explicit canonical key ordering and canonical-length/minimal integer forms checks. + +### Gap D: Extra section preservation + +Observed: +- Decoder tolerates unknown non-critical sections, but `Bundle` model/encoder drops them on re-encode. + +Sane resolution: +- Add `bundleExtraSections :: [SectionEntry+Bytes]` if round-trip preservation is desired. + +### Gap E: Section version not enforced + +Observed: +- Section entry `Version` is parsed but unused. + +Sane resolution: +- Enforce known version matrix (e.g., manifest v1, nodes v1), or explicitly document “advisory only”. + +### Gap F: Runtime capability policy is hard fail + +Observed: +- Any non-empty capabilities list is rejected. + +Sane resolution: +- Keep strict for now, but define capability negotiation strategy for v1.1+ (unknown capabilities => reject unless explicitly allowed by host policy). + +### Gap G: Error handling style in import/export path + +Observed: +- Several paths throw `error` for malformed data/store misses. + +Sane resolution: +- Return `Either`-style typed errors through public API (`decode`, `verify`, `import`), reserve exceptions for truly internal faults. + +--- + +## 9. Conformance checklist (v1 current) + +A conforming v1 reader/writer for this profile should: + +- Implement the 32-byte header and 60-byte section records exactly. +- Support required sections 1 and 2. +- Verify section digests with SHA-256. +- Decode/encode manifest CBOR matching the field model above. +- Parse nodes section and validate node payload structure. +- Recompute and verify node hashes. +- Enforce complete closure for roots. +- Enforce manifest/runtime constants used by v1. + +--- + +## 10. Suggested follow-up docs + +To stabilize interoperability, add: + +1. `docs/arboricx-bundle-test-vectors.md` (golden header/manifest/nodes + expected hashes). +2. `docs/arboricx-bundle-errors.md` (normative error codes/strings). +3. `docs/arboricx-bundle-evolution.md` (rules for minor/major upgrades, capability negotiation, extra sections). diff --git a/docs/arborix-bundle-cbor-v1.md b/docs/arborix-bundle-cbor-v1.md @@ -1,339 +0,0 @@ -# Arborix Portable Bundle v1 (CBOR Manifest Profile) - -Status: **Draft, implementation-aligned** (derived from `src/Wire.hs` as of 2026-05-07) - -This document specifies the **actual on-wire format and validation behavior** currently implemented by `tricu` for Arborix bundles, with a focus on the newer CBOR manifest path. - ---- - -## 1. Scope - -This profile defines: - -1. The binary container envelope (header + section directory + section payloads). -2. The CBOR manifest section format. -3. The Merkle node section format. -4. Decode/verify/import behavior in `Wire.hs`. -5. Known gaps and sane resolutions. - -Non-goals: - -- tricu source parsing/lambda elimination/module semantics. -- Signature systems / trust policy. -- Compression codecs beyond `none`. - ---- - -## 2. Container format - -A bundle is a byte stream: - -``` -[32-byte header] -[section directory: section_count * 60 bytes] -[section payload bytes...] -``` - -### 2.1 Header (32 bytes) - -| Field | Size | Encoding | Value / Notes | -|---|---:|---|---| -| Magic | 8 | raw bytes | `41 52 42 4f 52 49 58 00` (`"ARBORIX\0"`) | -| Major | 2 | u16 BE | Must be `1` | -| Minor | 2 | u16 BE | Currently `0` | -| SectionCount | 4 | u32 BE | Number of section directory entries | -| Flags | 8 | u64 BE | Currently emitted as `0`; not interpreted | -| DirectoryOffset | 8 | u64 BE | Offset of section directory (currently `32`) | - -Reader behavior: -- Reject if total bytes < 32. -- Reject bad magic. -- Reject major != 1. - -### 2.2 Section directory entry (60 bytes each) - -| Field | Size | Encoding | Notes | -|---|---:|---|---| -| Type | 4 | u32 BE | e.g. 1=manifest, 2=nodes | -| Version | 2 | u16 BE | Currently emitted as `1`; not enforced on read | -| Flags | 2 | u16 BE | bit0 = critical | -| Compression | 2 | u16 BE | `0` = none (required) | -| DigestAlgorithm | 2 | u16 BE | `1` = SHA-256 (required) | -| Offset | 8 | u64 BE | Absolute byte offset | -| Length | 8 | u64 BE | Section payload length | -| Digest | 32 | raw bytes | SHA-256 of section bytes | - -Reader behavior: -- Reject unknown **critical** section types. -- Reject compression != 0. -- Reject digest algorithm != 1. -- Reject out-of-bounds sections. -- Reject digest mismatch. - -### 2.3 Required section types - -| Type | Name | Required | -|---:|---|---| -| 1 | manifest | yes | -| 2 | nodes | yes | - -Decode currently rejects duplicate section type 1 or 2. - ---- - -## 3. Manifest section (CBOR) - -Manifest bytes are CBOR-encoded map data (using `cborg`). - -### 3.1 Top-level manifest schema - -Top-level map has **exactly 8 keys** in this exact decode order in current implementation: - -1. `schema` (text) -2. `bundleType` (text) -3. `tree` (map) -4. `runtime` (map) -5. `closure` (text: `"complete"|"partial"`) -6. `roots` (array) -7. `exports` (array) -8. `metadata` (map) - -> Important: Current decoder is order-strict; it expects keys in this sequence. - -### 3.2 Nested structures - -#### `tree` map (3 keys, order-strict) -- `calculus`: text -- `nodeHash`: map -- `nodePayload`: text - -`nodeHash` map (2 keys, order-strict): -- `algorithm`: text -- `domain`: text - -#### `runtime` map (4 keys, order-strict) -- `semantics`: text -- `evaluation`: text -- `abi`: text -- `capabilities`: array(text) - -#### `roots` array of maps -Each root map has 2 keys (order-strict): -- `hash`: bytes (raw 32-byte hash payload encoded as CBOR byte string) -- `role`: text - -#### `exports` array of maps -Each export map has 4 keys (order-strict): -- `name`: text -- `root`: bytes (32-byte hash) -- `kind`: text -- `abi`: text - -#### `metadata` map -Flexible key set; decoded as map(text -> text), then projected into optional fields: -- `package` -- `version` -- `description` -- `license` -- `createdBy` - -Unknown metadata keys are ignored. - -### 3.3 Default emitted manifest values - -Writers in `Wire.hs` currently emit: - -- `schema = "arborix.bundle.manifest.v1"` -- `bundleType = "tree-calculus-executable-object"` -- `tree.calculus = "tree-calculus.v1"` -- `tree.nodeHash.algorithm = "sha256"` -- `tree.nodeHash.domain = "arborix.merkle.node.v1"` -- `tree.nodePayload = "arborix.merkle.payload.v1"` -- `runtime.semantics = "tree-calculus.v1"` -- `runtime.evaluation = "normal-order"` -- `runtime.abi = "arborix.abi.tree.v1"` -- `runtime.capabilities = []` -- `closure = "complete"` -- `metadata.createdBy = "arborix"` - ---- - -## 4. Nodes section (binary) - -Node section payload layout: - -``` -node_count: u64 BE -repeat node_count times: - hash: 32 bytes - payload_len: u32 BE - payload: payload_len bytes -``` - -Node payload grammar: - -- `0x00` => Leaf -- `0x01 || child_hash(32)` => Stem -- `0x02 || left_hash(32)||right(32)` => Fork - -Section decoder rejects: -- duplicate node hashes, -- truncated entries, -- payload overruns, -- trailing bytes after final node. - ---- - -## 5. Verification behavior (`verifyBundle`) - -`verifyBundle` enforces all of: - -1. bundle version >= 1. -2. bundle has at least one node. -3. manifest constants match hardcoded v1 values (schema/type/calculus/hash algo/domain/payload/runtime semantics/ABI). -4. runtime capabilities must be empty. -5. closure must be `complete`. -6. manifest has at least one root and one export. -7. root sets in `bundleRoots` and `manifest.roots` must match exactly. -8. each root and export root exists in node map. -9. each node payload deserializes and re-hashes to declared node hash. -10. all referenced child hashes exist. -11. full closure reachability from roots succeeds. - -`importBundle` runs decode + verify before storing nodes. - ---- - -## 6. Export/import semantics - -### 6.1 Export - -`exportNamedBundle`: -- Traverses reachable nodes for each requested root hash. -- Builds node map. -- Builds default manifest and CBOR bytes. -- Emits two sections (manifest, nodes). - -`exportBundle` auto-names exports: -- 1 root => `root` -- N>1 => `root0`, `root1`, ... - -### 6.2 Import - -`importBundle`: -1. Decode bundle. -2. Verify bundle. -3. Insert all node payloads into content store. -4. For each manifest export: reconstruct tree by export root and store name binding in DB. -5. Return bundle root list. - ---- - -## 7. Determinism properties - -Current implementation is deterministic for identical logical input because: -- Node map serialized in ascending hash order (`Map.toAscList`). -- Field order in manifest encoding is fixed by code. -- Section ordering is fixed: manifest then nodes. - -So repeated exports of same roots produce byte-identical bundles. - ---- - -## 8. Known gaps and sane resolutions - -These are important design gaps visible from current code. - -### Gap A: Node hash domain mismatch risk (critical) - -Status: **resolved in current codebase**. - -What was wrong: -- Manifest declared `tree.nodeHash.domain = "arborix.merkle.node.v1"`. -- Hashing implementation previously used `"tricu.merkle.node.v1"`. - -Current state: -- Haskell hashing now uses `"arborix.merkle.node.v1"`. -- JS reference runtime hashing now uses `"arborix.merkle.node.v1"`. -- JS manifest validation now requires `"arborix.merkle.node.v1"`. - -Remaining recommendation: -- Keep hash-domain constants centralized/shared to prevent future drift. -- Add explicit test vectors for Leaf/Stem/Fork hashes under the Arborix domain. - -### Gap B: CBOR decode is order-strict, not generic-map tolerant - -Observed: -- Decoder expects exact key order for most maps. - -Impact: -- Another canonical CBOR writer that reorders keys may decode-fail even if semantically equivalent. - -Sane resolution: -- For v1 compatibility, decode maps as unordered key/value collections, require key presence and types, and reject unknown keys only where desired. -- Keep writer deterministic, but relax reader. - -### Gap C: “Canonical CBOR” claim is stronger than implementation - -Observed: -- Writer uses fixed order but does not explicitly sort keys per RFC 8949 canonical ordering rules. - -Sane resolution: -- Either (a) rename as “deterministic CBOR” profile, or (b) implement explicit canonical key ordering and canonical-length/minimal integer forms checks. - -### Gap D: Extra section preservation - -Observed: -- Decoder tolerates unknown non-critical sections, but `Bundle` model/encoder drops them on re-encode. - -Sane resolution: -- Add `bundleExtraSections :: [SectionEntry+Bytes]` if round-trip preservation is desired. - -### Gap E: Section version not enforced - -Observed: -- Section entry `Version` is parsed but unused. - -Sane resolution: -- Enforce known version matrix (e.g., manifest v1, nodes v1), or explicitly document “advisory only”. - -### Gap F: Runtime capability policy is hard fail - -Observed: -- Any non-empty capabilities list is rejected. - -Sane resolution: -- Keep strict for now, but define capability negotiation strategy for v1.1+ (unknown capabilities => reject unless explicitly allowed by host policy). - -### Gap G: Error handling style in import/export path - -Observed: -- Several paths throw `error` for malformed data/store misses. - -Sane resolution: -- Return `Either`-style typed errors through public API (`decode`, `verify`, `import`), reserve exceptions for truly internal faults. - ---- - -## 9. Conformance checklist (v1 current) - -A conforming v1 reader/writer for this profile should: - -- Implement the 32-byte header and 60-byte section records exactly. -- Support required sections 1 and 2. -- Verify section digests with SHA-256. -- Decode/encode manifest CBOR matching the field model above. -- Parse nodes section and validate node payload structure. -- Recompute and verify node hashes. -- Enforce complete closure for roots. -- Enforce manifest/runtime constants used by v1. - ---- - -## 10. Suggested follow-up docs - -To stabilize interoperability, add: - -1. `docs/arborix-bundle-test-vectors.md` (golden header/manifest/nodes + expected hashes). -2. `docs/arborix-bundle-errors.md` (normative error codes/strings). -3. `docs/arborix-bundle-evolution.md` (rules for minor/major upgrades, capability negotiation, extra sections). diff --git a/ext/js/package.json b/ext/js/package.json @@ -1,17 +1,17 @@ { - "name": "arborix-runtime", + "name": "arboricx-runtime", "version": "0.1.0", - "description": "Arborix portable bundle runtime — JavaScript reference implementation", + "description": "Arboricx portable bundle runtime — JavaScript reference implementation", "type": "module", "main": "src/bundle.js", "bin": { - "arborix-run": "src/cli.js" + "arboricx-run": "src/cli.js" }, "scripts": { "test": "node --test test/*.test.js", "inspect": "node src/cli.js inspect", "run": "node src/cli.js run" }, - "keywords": ["arborix", "tree-calculus", "trie", "runtime"], + "keywords": ["arboricx", "tree-calculus", "trie", "runtime"], "license": "MIT" } diff --git a/ext/js/src/bundle.js b/ext/js/src/bundle.js @@ -1,9 +1,9 @@ /** - * bundle.js — Parse an Arborix portable bundle binary into a JavaScript object. + * bundle.js — Parse an Arboricx portable bundle binary into a JavaScript object. * * Format (v1): * Header (32 bytes): - * Magic 8B "ARBORIX\0" + * Magic 8B "ARBORICX" * Major 2B u16 BE (must be 1) * Minor 2B u16 BE * SectionCount 4B u32 BE @@ -27,7 +27,7 @@ import { decodeCbor } from "./cbor.js"; // ── Constants ─────────────────────────────────────────────────────────────── -const MAGIC = Buffer.from([0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x58, 0x00]); // "ARBORIX\0" +const MAGIC = Buffer.from([0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x43, 0x58]); // "ARBORICX" const HEADER_LENGTH = 32; const SECTION_ENTRY_LENGTH = 60; const SECTION_MANIFEST = 1; @@ -69,7 +69,7 @@ export function parseBundle(buffer) { // Check magic if (!buffer.slice(0, 8).equals(MAGIC)) { - throw new Error("invalid magic: expected ARBORIX\\0"); + throw new Error("invalid magic: expected ARBORICX"); } // Parse header diff --git a/ext/js/src/cbor.js b/ext/js/src/cbor.js @@ -1,5 +1,5 @@ /** - * cbor.js — Minimal CBOR decoder for the Arborix manifest format. + * cbor.js — Minimal CBOR decoder for the Arboricx manifest format. * * Decodes the canonical CBOR produced by the Haskell cborg library: * - Maps: major type 5 (0xa0 + length) diff --git a/ext/js/src/cli.js b/ext/js/src/cli.js @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * cli.js — Minimal CLI for inspecting and running Arborix bundles. + * cli.js — Minimal CLI for inspecting and running Arboricx bundles. * * Usage: * node cli.js inspect <bundle> @@ -240,7 +240,7 @@ switch (command) { break; } default: - console.log("Arborix JS Runtime"); + console.log("Arboricx JS Runtime"); console.log(""); console.log("Usage:"); console.log(" node cli.js inspect <bundle>"); diff --git a/ext/js/src/manifest.js b/ext/js/src/manifest.js @@ -13,7 +13,7 @@ * Throws on violation. */ export function validateManifest(manifest) { - if (manifest.schema !== "arborix.bundle.manifest.v1") { + if (manifest.schema !== "arboricx.bundle.manifest.v1") { throw new Error( `unsupported manifest schema: ${manifest.schema}` ); @@ -33,12 +33,12 @@ export function validateManifest(manifest) { `unsupported node hash algorithm: ${tree.nodeHash.algorithm}` ); } - if (tree.nodeHash.domain !== "arborix.merkle.node.v1") { + if (tree.nodeHash.domain !== "arboricx.merkle.node.v1") { throw new Error( `unsupported node hash domain: ${tree.nodeHash.domain}` ); } - if (tree.nodePayload !== "arborix.merkle.payload.v1") { + if (tree.nodePayload !== "arboricx.merkle.payload.v1") { throw new Error(`unsupported node payload: ${tree.nodePayload}`); } @@ -46,7 +46,7 @@ export function validateManifest(manifest) { if (runtime.semantics !== "tree-calculus.v1") { throw new Error(`unsupported runtime semantics: ${runtime.semantics}`); } - if (runtime.abi !== "arborix.abi.tree.v1") { + if (runtime.abi !== "arboricx.abi.tree.v1") { throw new Error(`unsupported runtime ABI: ${runtime.abi}`); } if (runtime.capabilities && runtime.capabilities.length > 0) { diff --git a/ext/js/src/merkle.js b/ext/js/src/merkle.js @@ -7,14 +7,14 @@ * Fork: 0x02 || left_hash (32 bytes raw) || right_hash (32 bytes raw) * * Hash computation: - * hash = SHA256( "arborix.merkle.node.v1" || 0x00 || node_payload ) + * hash = SHA256( "arboricx.merkle.node.v1" || 0x00 || node_payload ) */ import { createHash } from "node:crypto"; // ── Constants ─────────────────────────────────────────────────────────────── -const DOMAIN_TAG = "arborix.merkle.node.v1"; +const DOMAIN_TAG = "arboricx.merkle.node.v1"; const HASH_LENGTH = 32; // raw hash bytes const HEX_LENGTH = 64; // hex-encoded hash length diff --git a/ext/js/test/bundle.test.js b/ext/js/test/bundle.test.js @@ -19,7 +19,7 @@ const fixtureDir = "../../test/fixtures"; describe("bundle parsing", () => { it("valid bundle parses header and sections", () => { const bundle = parseBundle( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); strictEqual(bundle.version, "1.0"); strictEqual(bundle.sectionCount, 2); @@ -29,23 +29,23 @@ describe("bundle parsing", () => { it("parseManifest returns valid manifest", () => { const manifest = parseManifest( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); - strictEqual(manifest.schema, "arborix.bundle.manifest.v1"); + strictEqual(manifest.schema, "arboricx.bundle.manifest.v1"); strictEqual(manifest.bundleType, "tree-calculus-executable-object"); strictEqual(manifest.closure, "complete"); strictEqual(manifest.tree.calculus, "tree-calculus.v1"); strictEqual(manifest.tree.nodeHash.algorithm, "sha256"); - strictEqual(manifest.tree.nodeHash.domain, "arborix.merkle.node.v1"); + strictEqual(manifest.tree.nodeHash.domain, "arboricx.merkle.node.v1"); strictEqual(manifest.runtime.semantics, "tree-calculus.v1"); - strictEqual(manifest.runtime.abi, "arborix.abi.tree.v1"); + strictEqual(manifest.runtime.abi, "arboricx.abi.tree.v1"); }); }); describe("hash verification", () => { it("valid bundle nodes verify", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodes(data); const { verified } = verifyNodeHashes(nodeMap); @@ -62,20 +62,20 @@ describe("errors", () => { it("unsupported version fails", () => { const buf = Buffer.alloc(32, 0); - buf.write("ARBORIX\0", 0, 8); + buf.write("ARBORICX", 0, 8); buf.writeUInt16BE(2, 8); // major version 2 throws(() => parseBundle(buf), /unsupported bundle major version/); }); it("bad section digest fails", () => { - const buf = readFileSync(`${fixtureDir}/id.arborix`); + const buf = readFileSync(`${fixtureDir}/id.arboricx`); // Corrupt one byte in the manifest section buf[152] ^= 0x01; throws(() => parseBundle(buf), /digest mismatch/); }); it("truncated bundle fails", () => { - const buf = readFileSync(`${fixtureDir}/id.arborix`); + const buf = readFileSync(`${fixtureDir}/id.arboricx`); const truncated = buf.slice(0, 40); throws(() => parseBundle(truncated), /truncated/); }); @@ -83,33 +83,33 @@ describe("errors", () => { it("missing nodes section fails", () => { // Build a bundle with only manifest entry in the directory (1 section instead of 2) const header = Buffer.alloc(32, 0); - header.write("ARBORIX\0", 0, 8); + header.write("ARBORICX", 0, 8); header.writeUInt16BE(1, 8); // major version header.writeUInt16BE(0, 10); // minor version header.writeUInt32BE(1, 12); // 1 section // Build a manifest JSON const manifestObj = { - schema: "arborix.bundle.manifest.v1", + schema: "arboricx.bundle.manifest.v1", bundleType: "tree-calculus-executable-object", tree: { calculus: "tree-calculus.v1", nodeHash: { algorithm: "sha256", - domain: "arborix.merkle.node.v1" + domain: "arboricx.merkle.node.v1" }, - nodePayload: "arborix.merkle.payload.v1" + nodePayload: "arboricx.merkle.payload.v1" }, runtime: { semantics: "tree-calculus.v1", evaluation: "normal-order", - abi: "arborix.abi.tree.v1", + abi: "arboricx.abi.tree.v1", capabilities: [] }, closure: "complete", roots: [{ hash: Buffer.alloc(32).toString("hex"), role: "default" }], - exports: [{ name: "root", root: Buffer.alloc(32).toString("hex"), kind: "term", abi: "arborix.abi.tree.v1" }], - metadata: { createdBy: "arborix" } + exports: [{ name: "root", root: Buffer.alloc(32).toString("hex"), kind: "term", abi: "arboricx.abi.tree.v1" }], + metadata: { createdBy: "arboricx" } }; const manifestJson = JSON.stringify(manifestObj); const manifestBytes = Buffer.from(manifestJson); diff --git a/ext/js/test/merkle.test.js b/ext/js/test/merkle.test.js @@ -51,35 +51,35 @@ describe("merkle — computeNodeHash", () => { strictEqual(hash.length, 64); }); - it("Leaf hash matches expected Arborix domain", () => { + it("Leaf hash matches expected Arboricx domain", () => { const leaf = { type: "leaf" }; const hash = computeNodeHash(leaf); - strictEqual(hash, "e54db458aa8e94782f7c61ad6c1f19a1c0c6fca7ffe53674f0d2bc5ff7ab02ff"); + strictEqual(hash, "92b8a9796dbeafbcd36757535876256392170d137bf36b319d77f11a37112158"); }); }); describe("merkle — node section parsing", () => { const fixtureDir = "../../test/fixtures"; - it("parses id.arborix with correct node count", () => { + it("parses id.arboricx with correct node count", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodeSection(data); strictEqual(nodeMap.size, 4); }); - it("parses true.arborix with correct node count", () => { + it("parses true.arboricx with correct node count", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/true.arborix`) + readFileSync(`${fixtureDir}/true.arboricx`) ); const { nodeMap } = parseNodeSection(data); strictEqual(nodeMap.size, 2); }); - it("parses false.arborix with correct node count", () => { + it("parses false.arboricx with correct node count", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/false.arborix`) + readFileSync(`${fixtureDir}/false.arboricx`) ); const { nodeMap } = parseNodeSection(data); strictEqual(nodeMap.size, 1); @@ -89,29 +89,29 @@ describe("merkle — node section parsing", () => { describe("merkle — hash verification", () => { const fixtureDir = "../../test/fixtures"; - it("id.arborix nodes all verify", () => { + it("id.arboricx nodes all verify", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodeSection(data); const { verified, mismatches } = verifyNodeHashes(nodeMap); - ok(verified, "id.arborix node hashes should verify"); + ok(verified, "id.arboricx node hashes should verify"); strictEqual(mismatches.length, 0); }); - it("true.arborix nodes all verify", () => { + it("true.arboricx nodes all verify", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/true.arborix`) + readFileSync(`${fixtureDir}/true.arboricx`) ); const { nodeMap } = parseNodeSection(data); const { verified, mismatches } = verifyNodeHashes(nodeMap); - ok(verified, "true.arborix node hashes should verify"); + ok(verified, "true.arboricx node hashes should verify"); strictEqual(mismatches.length, 0); }); it("corrupted node payload fails hash verification", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodeSection(data); // Find a stem node to corrupt @@ -137,23 +137,23 @@ describe("merkle — hash verification", () => { describe("merkle — closure verification", () => { const fixtureDir = "../../test/fixtures"; - it("id.arborix has complete closure", () => { + it("id.arboricx has complete closure", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodeSection(data); const { complete, missing } = verifyClosure(nodeMap); - ok(complete, "id.arborix should have complete closure"); + ok(complete, "id.arboricx should have complete closure"); strictEqual(missing.length, 0); }); it("verifyRootClosure checks transitive reachability", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const { nodeMap } = parseNodeSection(data); // Use the actual root hash from the fixture's manifest - const manifest = parseManifest(readFileSync(`${fixtureDir}/id.arborix`)); + const manifest = parseManifest(readFileSync(`${fixtureDir}/id.arboricx`)); const rootHash = manifest.exports[0].root; const { complete, missingRoots } = verifyRootClosure(nodeMap, rootHash); ok(complete, "root should be reachable"); @@ -162,7 +162,7 @@ describe("merkle — closure verification", () => { it("parseNodeSection returns correct node count", () => { const data = bundleParseNodeSection( - readFileSync(`${fixtureDir}/id.arborix`) + readFileSync(`${fixtureDir}/id.arboricx`) ); const result = parseNodeSection(data); strictEqual(result.count, 4); diff --git a/ext/js/test/run-bundle.test.js b/ext/js/test/run-bundle.test.js @@ -9,8 +9,8 @@ import { buildTreeFromNodeMap } from "../src/cli.js"; const fixtureDir = "../../test/fixtures"; -describe("run bundle — id.arborix", () => { - const bundle = readFileSync(`${fixtureDir}/id.arborix`); +describe("run bundle — id.arboricx", () => { + const bundle = readFileSync(`${fixtureDir}/id.arboricx`); const manifest = parseManifest(bundle); const nodeSectionData = bundleParseNodeSection(bundle); const { nodeMap } = parseNodes(nodeSectionData); @@ -37,8 +37,8 @@ describe("run bundle — id.arborix", () => { }); }); -describe("run bundle — true.arborix", () => { - const bundle = readFileSync(`${fixtureDir}/true.arborix`); +describe("run bundle — true.arboricx", () => { + const bundle = readFileSync(`${fixtureDir}/true.arboricx`); const manifest = parseManifest(bundle); const nodeSectionData = bundleParseNodeSection(bundle); const { nodeMap } = parseNodes(nodeSectionData); @@ -61,8 +61,8 @@ describe("run bundle — true.arborix", () => { }); }); -describe("run bundle — false.arborix", () => { - const bundle = readFileSync(`${fixtureDir}/false.arborix`); +describe("run bundle — false.arboricx", () => { + const bundle = readFileSync(`${fixtureDir}/false.arboricx`); const manifest = parseManifest(bundle); const nodeSectionData = bundleParseNodeSection(bundle); const { nodeMap } = parseNodes(nodeSectionData); @@ -83,8 +83,8 @@ describe("run bundle — false.arborix", () => { }); }); -describe("run bundle — notQ.arborix", () => { - const bundle = readFileSync(`${fixtureDir}/notQ.arborix`); +describe("run bundle — notQ.arboricx", () => { + const bundle = readFileSync(`${fixtureDir}/notQ.arboricx`); const manifest = parseManifest(bundle); const nodeSectionData = bundleParseNodeSection(bundle); const { nodeMap } = parseNodes(nodeSectionData); @@ -100,7 +100,7 @@ describe("run bundle — notQ.arborix", () => { }); describe("run bundle — missing export", () => { - const bundle = readFileSync(`${fixtureDir}/id.arborix`); + const bundle = readFileSync(`${fixtureDir}/id.arboricx`); const manifest = parseManifest(bundle); it("nonexistent export fails clearly", () => { @@ -109,8 +109,8 @@ describe("run bundle — missing export", () => { }); describe("run bundle — auto-select", () => { - // true.arborix has only one export, should auto-select - const bundle = readFileSync(`${fixtureDir}/true.arborix`); + // true.arboricx has only one export, should auto-select + const bundle = readFileSync(`${fixtureDir}/true.arboricx`); const manifest = parseManifest(bundle); it("single export auto-selects", () => { diff --git a/flake.nix b/flake.nix @@ -36,8 +36,6 @@ checks.${packageName} = tricuPackageTests; checks.default = tricuPackageTests; - defaultPackage = self.packages.${system}.default; - devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ haskellPackages.cabal-install @@ -51,7 +49,6 @@ tricuPackage ]; }; - devShell = self.devShells.${system}.default; packages.${containerPackageName} = pkgs.dockerTools.buildImage { name = "tricu"; diff --git a/lib/arboricx.tri b/lib/arboricx.tri @@ -0,0 +1,654 @@ +!import "base.tri" !Local +!import "list.tri" !Local +!import "bytes.tri" !Local +!import "binary.tri" !Local + +arboricxMagic = [(65) (82) (66) (79) (82) (73) (67) (88)] +arboricxMajorVersion = [(0) (1)] +arboricxMinorVersion = [(0) (0)] +arboricxManifestSectionId = [(0) (0) (0) (1)] +arboricxNodesSectionId = [(0) (0) (0) (2)] + +errMissingSection = 4 +errUnsupportedVersion = 5 +errDuplicateSection = 6 +errDuplicateNode = 7 +errInvalidNodePayload = 8 +errMissingNode = 9 + +nodePayloadLeafTag = 0 +nodePayloadStemTag = 1 +nodePayloadForkTag = 2 + +readArboricxMagic = (bs : expectBytes arboricxMagic bs) + +readArboricxHeader = (bs : + bindResult (readArboricxMagic bs) + (_ afterMagic : + bindResult (readBytes 2 afterMagic) + (majorVersion afterMajor : + bindResult (readBytes 2 afterMajor) + (minorVersion afterMinor : + bindResult (readBytes 4 afterMinor) + (sectionCount afterSectionCount : + bindResult (readBytes 8 afterSectionCount) + (flags afterFlags : + bindResult (readBytes 8 afterFlags) + (dirOffset afterDirOffset : + ok + (pair majorVersion + (pair minorVersion + (pair sectionCount + (pair flags dirOffset)))) + afterDirOffset))))))) + +readSectionRecord = (bs : + bindResult (readBytes 4 bs) + (sectionId afterSectionId : + bindResult (readBytes 2 afterSectionId) + (sectionVersion afterSectionVersion : + bindResult (readBytes 2 afterSectionVersion) + (sectionFlags afterSectionFlags : + bindResult (readBytes 2 afterSectionFlags) + (compression afterCompression : + bindResult (readBytes 2 afterCompression) + (digestAlgorithm afterDigestAlgorithm : + bindResult (readBytes 8 afterDigestAlgorithm) + (offset afterOffset : + bindResult (readBytes 8 afterOffset) + (length afterLength : + bindResult (readBytes 32 afterLength) + (digest afterDigest : + ok + (pair sectionId + (pair sectionVersion + (pair sectionFlags + (pair compression + (pair digestAlgorithm + (pair offset + (pair length digest))))))) + afterDigest))))))))) + +readSectionDirectory_ = y (self bs sectionCount i acc : + matchBool + (ok (reverse acc) bs) + (bindResult (readSectionRecord bs) + (sectionRecord afterSectionRecord : + self afterSectionRecord sectionCount (succ i) (pair sectionRecord acc))) + (equal? i sectionCount)) + +readSectionDirectory = (sectionCount bs : readSectionDirectory_ bs sectionCount 0 t) + +sectionRecordId = (sectionRecord : + matchPair + (sectionId _ : sectionId) + sectionRecord) + +sectionRecordVersion = (sectionRecord : + matchPair + (_ payload : + matchPair + (sectionVersion _ : sectionVersion) + payload) + sectionRecord) + +sectionRecordFlags = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (sectionFlags _ : sectionFlags) + payload2) + payload) + sectionRecord) + +sectionRecordCompression = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (compression _ : compression) + payload3) + payload2) + payload) + sectionRecord) + +sectionRecordDigestAlgorithm = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (_ payload4 : + matchPair + (digestAlgorithm _ : digestAlgorithm) + payload4) + payload3) + payload2) + payload) + sectionRecord) + +sectionRecordOffset = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (_ payload4 : + matchPair + (_ payload5 : + matchPair + (offset _ : offset) + payload5) + payload4) + payload3) + payload2) + payload) + sectionRecord) + +sectionRecordLength = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (_ payload4 : + matchPair + (_ payload5 : + matchPair + (_ payload6 : + matchPair + (length _ : length) + payload6) + payload5) + payload4) + payload3) + payload2) + payload) + sectionRecord) + +sectionRecordDigest = (sectionRecord : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (_ payload4 : + matchPair + (_ payload5 : + matchPair + (_ payload6 : + matchPair + (_ digest : digest) + payload6) + payload5) + payload4) + payload3) + payload2) + payload) + sectionRecord) + +lookupSectionRecord_ = y (self directory sectionId : + matchList + nothing + (sectionRecord rest : + matchBool + (just sectionRecord) + (self rest sectionId) + (bytesEq? sectionId (sectionRecordId sectionRecord))) + directory) + +lookupSectionRecord = (sectionId directory : lookupSectionRecord_ directory sectionId) + +sectionDirectoryHasId?_ = y (self directory sectionId : + matchList + false + (sectionRecord rest : + or? + (bytesEq? sectionId (sectionRecordId sectionRecord)) + (self rest sectionId)) + directory) + +sectionDirectoryHasId? = (sectionId directory : sectionDirectoryHasId?_ directory sectionId) + +sectionDirectoryHasDuplicateIds? = y (self directory : + matchList + false + (sectionRecord rest : + or? + (sectionDirectoryHasId?_ rest (sectionRecordId sectionRecord)) + (self rest)) + directory) + +validateSectionDirectory = (directory rest : + matchBool + (err errDuplicateSection rest) + (ok directory rest) + (sectionDirectoryHasDuplicateIds? directory)) + +byteSlice = (offset length bytes : bytesTake length (bytesDrop offset bytes)) + +natMake = (bit rest : + matchBool + 0 + (pair bit rest) + (and? (equal? bit 0) (equal? rest 0))) + +natAdd = y (self a b : + triage + b + (_ : b) + (aBit aRest : + triage + a + (_ : a) + (bBit bRest : + matchBool + (natMake 0 (succ (self aRest bRest))) + (natMake (matchBool (matchBool 0 1 bBit) (matchBool 1 0 bBit) aBit) + (self aRest bRest)) + (and? (equal? aBit 1) (equal? bBit 1))) + b) + a) + +natDouble = (n : matchBool 0 (pair 0 n) (equal? n 0)) + +natTimes256 = (n : + natDouble + (natDouble + (natDouble + (natDouble + (natDouble + (natDouble + (natDouble + (natDouble n)))))))) + +byteNatShiftAppend_ = y (self byte acc i : + matchBool + acc + (triage + (natMake 0 (self 0 acc (succ i))) + (_ : acc) + (bit rest : natMake bit (self rest acc (succ i))) + byte) + (equal? i 8)) + +byteNatShiftAppend = (byte acc : byteNatShiftAppend_ byte acc 0) + +beBytesToNat = (bytes : + foldl + (acc byte : byteNatShiftAppend byte acc) + 0 + bytes) + +u32BEBytesToNat = beBytesToNat +u64BEBytesToNat = beBytesToNat + +arboricxHeaderMajorVersion = (header : + matchPair + (majorVersion _ : majorVersion) + header) + +arboricxHeaderMinorVersion = (header : + matchPair + (_ payload : + matchPair + (minorVersion _ : minorVersion) + payload) + header) + +arboricxHeaderSectionCount = (header : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (sectionCount _ : sectionCount) + payload2) + payload) + header) + +arboricxHeaderFlags = (header : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (flags _ : flags) + payload3) + payload2) + payload) + header) + +arboricxHeaderDirOffset = (header : + matchPair + (_ payload : + matchPair + (_ payload2 : + matchPair + (_ payload3 : + matchPair + (_ dirOffset : dirOffset) + payload3) + payload2) + payload) + header) + +validateArboricxHeader = (header rest : + matchBool + (ok header rest) + (err errUnsupportedVersion rest) + (and? + (bytesEq? arboricxMajorVersion (arboricxHeaderMajorVersion header)) + (bytesEq? arboricxMinorVersion (arboricxHeaderMinorVersion header)))) + +readArboricxContainer = (bs : + bindResult (readArboricxHeader bs) + (header afterHeader : + bindResult (validateArboricxHeader header afterHeader) + (validHeader afterValidHeader : + bindResult (readSectionDirectory + (u32BEBytesToNat (arboricxHeaderSectionCount validHeader)) + (bytesDrop (u64BEBytesToNat (arboricxHeaderDirOffset validHeader)) bs)) + (directory afterDirectory : + bindResult (validateSectionDirectory directory afterDirectory) + (validDirectory afterValidDirectory : + ok (pair validHeader validDirectory) afterValidDirectory))))) + +sectionRecordOffsetNat = (sectionRecord : + u64BEBytesToNat (sectionRecordOffset sectionRecord)) + +sectionRecordLengthNat = (sectionRecord : + u64BEBytesToNat (sectionRecordLength sectionRecord)) + +extractSectionBytes = (sectionRecord containerBytes : + byteSlice + (sectionRecordOffsetNat sectionRecord) + (sectionRecordLengthNat sectionRecord) + containerBytes) + +extractSectionBytesResult = (sectionRecord containerBytes rest : + (sectionBytes : + matchBool + (ok sectionBytes rest) + (err errUnexpectedEof rest) + (equal? (bytesLength sectionBytes) (sectionRecordLengthNat sectionRecord))) + (extractSectionBytes sectionRecord containerBytes)) + +lookupSectionBytes = (sectionId directory containerBytes : + triage + nothing + (sectionRecord : just (extractSectionBytes sectionRecord containerBytes)) + (_ _ : nothing) + (lookupSectionRecord sectionId directory)) + +sectionBytesOrErr = (sectionId directory containerBytes rest : + triage + (err errMissingSection rest) + (sectionRecord : extractSectionBytesResult sectionRecord containerBytes rest) + (_ _ : err errMissingSection rest) + (lookupSectionRecord sectionId directory)) + +readArboricxSectionBytes = (sectionId bs : + bindResult (readArboricxContainer bs) + (container afterContainer : + matchPair + (_ directory : sectionBytesOrErr sectionId directory bs afterContainer) + container)) + +readArboricxRequiredSections = (bs : + bindResult (readArboricxContainer bs) + (container afterContainer : + matchPair + (_ directory : + bindResult (sectionBytesOrErr arboricxManifestSectionId directory bs afterContainer) + (manifestBytes _ : + bindResult (sectionBytesOrErr arboricxNodesSectionId directory bs afterContainer) + (nodesBytes _ : + ok (pair manifestBytes nodesBytes) afterContainer))) + container)) + +readNodeRecord = (bs : + bindResult (readBytes 32 bs) + (nodeHash afterNodeHash : + bindResult (readBytes 4 afterNodeHash) + (payloadLength afterPayloadLength : + bindResult (readBytes (u32BEBytesToNat payloadLength) afterPayloadLength) + (payload afterPayload : + ok + (pair nodeHash + (pair payloadLength payload)) + afterPayload)))) + +nodeRecordHash = (nodeRecord : + matchPair + (nodeHash _ : nodeHash) + nodeRecord) + +nodeRecordPayloadLength = (nodeRecord : + matchPair + (_ payload : + matchPair + (payloadLength _ : payloadLength) + payload) + nodeRecord) + +nodeRecordPayload = (nodeRecord : + matchPair + (_ payload : + matchPair + (_ nodePayload : nodePayload) + payload) + nodeRecord) + +nodePayloadKind = (nodePayload : bytesHead nodePayload) + +nodePayloadHasTag? = (tag nodePayload : + triage + false + (actualTag : byteEq? actualTag tag) + (_ _ : false) + (nodePayloadKind nodePayload)) + +nodePayloadLeaf? = (nodePayload : bytesEq? [(0)] nodePayload) + +nodePayloadStem? = (nodePayload : + and? + (nodePayloadHasTag? nodePayloadStemTag nodePayload) + (equal? (bytesLength nodePayload) 33)) + +nodePayloadFork? = (nodePayload : + and? + (nodePayloadHasTag? nodePayloadForkTag nodePayload) + (equal? (bytesLength nodePayload) 65)) + +nodePayloadValid? = (nodePayload : + or? + (nodePayloadLeaf? nodePayload) + (or? + (nodePayloadStem? nodePayload) + (nodePayloadFork? nodePayload))) + +nodePayloadStemChildHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload)) +nodePayloadForkLeftHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload)) +nodePayloadForkRightHash = (nodePayload : bytesTake 32 (bytesDrop 33 nodePayload)) + +nodeRecordPayloadValid? = (nodeRecord : nodePayloadValid? (nodeRecordPayload nodeRecord)) + +nodeRecordsHaveInvalidPayload? = y (self nodeRecords : + matchList + false + (nodeRecord rest : + or? + (not? (nodeRecordPayloadValid? nodeRecord)) + (self rest)) + nodeRecords) + +nodeRecordsHaveHash? = y (self nodeRecords nodeHash : + matchList + false + (nodeRecord rest : + or? + (bytesEq? nodeHash (nodeRecordHash nodeRecord)) + (self rest nodeHash)) + nodeRecords) + +nodeRecordsHaveDuplicateHashes? = y (self nodeRecords : + matchList + false + (nodeRecord rest : + or? + (nodeRecordsHaveHash? rest (nodeRecordHash nodeRecord)) + (self rest)) + nodeRecords) + +lookupNodeRecord_ = y (self nodeRecords nodeHash : + matchList + nothing + (nodeRecord rest : + matchBool + (just nodeRecord) + (self rest nodeHash) + (bytesEq? nodeHash (nodeRecordHash nodeRecord))) + nodeRecords) + +lookupNodeRecord = (nodeHash nodeRecords : lookupNodeRecord_ nodeRecords nodeHash) + +nodeRecordChildHashes = (nodeRecord : + (nodePayload : + matchBool + t + (matchBool + (pair (nodePayloadStemChildHash nodePayload) t) + (pair (nodePayloadForkLeftHash nodePayload) + (pair (nodePayloadForkRightHash nodePayload) t)) + (nodePayloadStem? nodePayload)) + (nodePayloadLeaf? nodePayload)) + (nodeRecordPayload nodeRecord)) + +nodeHashPresent? = (nodeHash nodeRecords : nodeRecordsHaveHash? nodeRecords nodeHash) + +nodeChildHashesPresent? = y (self childHashes nodeRecords : + matchList + true + (childHash rest : + and? + (nodeHashPresent? childHash nodeRecords) + (self rest nodeRecords)) + childHashes) + +nodeRecordChildrenPresent? = (nodeRecord nodeRecords : + nodeChildHashesPresent? (nodeRecordChildHashes nodeRecord) nodeRecords) + +nodeRecordsClosed? = y (self nodeRecords allNodeRecords : + matchList + true + (nodeRecord rest : + and? + (nodeRecordChildrenPresent? nodeRecord allNodeRecords) + (self rest allNodeRecords)) + nodeRecords) + +validateNodeRecords = (nodeRecords rest : + matchBool + (err errInvalidNodePayload rest) + (matchBool + (err errDuplicateNode rest) + (matchBool + (ok nodeRecords rest) + (err errMissingNode rest) + (nodeRecordsClosed? nodeRecords nodeRecords)) + (nodeRecordsHaveDuplicateHashes? nodeRecords)) + (nodeRecordsHaveInvalidPayload? nodeRecords)) + +readNodeRecords_ = y (self bs nodeCount i acc : + matchBool + (ok (reverse acc) bs) + (bindResult (readNodeRecord bs) + (nodeRecord afterNodeRecord : + self afterNodeRecord nodeCount (succ i) (pair nodeRecord acc))) + (equal? i nodeCount)) + +readNodeRecords = (nodeCount bs : readNodeRecords_ bs nodeCount 0 t) + +readNodesSection = (bs : + bindResult (readBytes 8 bs) + (nodeCount afterNodeCount : + bindResult (readNodeRecords (u64BEBytesToNat nodeCount) afterNodeCount) + (nodeRecords afterNodeRecords : + bindResult (validateNodeRecords nodeRecords afterNodeRecords) + (validNodeRecords afterValidNodeRecords : + ok (pair nodeCount validNodeRecords) afterValidNodeRecords)))) + +readNodesSectionComplete = (bs : + bindResult (readNodesSection bs) + (nodesSection afterNodesSection : + matchBool + (ok nodesSection afterNodesSection) + (err errUnexpectedBytes afterNodesSection) + (bytesNil? afterNodesSection))) + +readArboricxNodesSection = (bs : + bindResult (readArboricxContainer bs) + (container afterContainer : + matchPair + (_ directory : + bindResult (sectionBytesOrErr arboricxNodesSectionId directory bs afterContainer) + (nodesBytes _ : + bindResult (readNodesSectionComplete nodesBytes) + (nodesSection _ : ok nodesSection afterContainer))) + container)) + +nodesSectionCount = (nodesSection : + matchPair + (nodeCount _ : nodeCount) + nodesSection) + +nodesSectionRecords = (nodesSection : + matchPair + (_ nodeRecords : nodeRecords) + nodesSection) + +nodeRecordToTreeWith = (self nodeRecords nodeRecord : + (nodePayload : + matchBool + (ok t t) + (matchBool + (bindResult (self (nodePayloadStemChildHash nodePayload) nodeRecords) + (child _ : ok (t child) t)) + (bindResult (self (nodePayloadForkLeftHash nodePayload) nodeRecords) + (left _ : + bindResult (self (nodePayloadForkRightHash nodePayload) nodeRecords) + (right _ : ok (pair left right) t))) + (nodePayloadStem? nodePayload)) + (nodePayloadLeaf? nodePayload)) + (nodeRecordPayload nodeRecord)) + +nodeHashToTree = y (self nodeHash nodeRecords : + triage + (err errMissingNode t) + (nodeRecord : nodeRecordToTreeWith self nodeRecords nodeRecord) + (_ _ : err errMissingNode t) + (lookupNodeRecord nodeHash nodeRecords)) + +readArboricxTreeFromHash = (rootHash bs : + bindResult (readArboricxNodesSection bs) + (nodesSection afterContainer : + bindResult (nodeHashToTree rootHash (nodesSectionRecords nodesSection)) + (tree _ : ok tree afterContainer))) + +readArboricxExecutableFromHash = readArboricxTreeFromHash diff --git a/lib/arborix.tri b/lib/arborix.tri @@ -1,654 +0,0 @@ -!import "base.tri" !Local -!import "list.tri" !Local -!import "bytes.tri" !Local -!import "binary.tri" !Local - -arborixMagic = [(65) (82) (66) (79) (82) (73) (88) (0)] -arborixMajorVersion = [(0) (1)] -arborixMinorVersion = [(0) (0)] -arborixManifestSectionId = [(0) (0) (0) (1)] -arborixNodesSectionId = [(0) (0) (0) (2)] - -errMissingSection = 4 -errUnsupportedVersion = 5 -errDuplicateSection = 6 -errDuplicateNode = 7 -errInvalidNodePayload = 8 -errMissingNode = 9 - -nodePayloadLeafTag = 0 -nodePayloadStemTag = 1 -nodePayloadForkTag = 2 - -readArborixMagic = (bs : expectBytes arborixMagic bs) - -readArborixHeader = (bs : - bindResult (readArborixMagic bs) - (_ afterMagic : - bindResult (readBytes 2 afterMagic) - (majorVersion afterMajor : - bindResult (readBytes 2 afterMajor) - (minorVersion afterMinor : - bindResult (readBytes 4 afterMinor) - (sectionCount afterSectionCount : - bindResult (readBytes 8 afterSectionCount) - (flags afterFlags : - bindResult (readBytes 8 afterFlags) - (dirOffset afterDirOffset : - ok - (pair majorVersion - (pair minorVersion - (pair sectionCount - (pair flags dirOffset)))) - afterDirOffset))))))) - -readSectionRecord = (bs : - bindResult (readBytes 4 bs) - (sectionId afterSectionId : - bindResult (readBytes 2 afterSectionId) - (sectionVersion afterSectionVersion : - bindResult (readBytes 2 afterSectionVersion) - (sectionFlags afterSectionFlags : - bindResult (readBytes 2 afterSectionFlags) - (compression afterCompression : - bindResult (readBytes 2 afterCompression) - (digestAlgorithm afterDigestAlgorithm : - bindResult (readBytes 8 afterDigestAlgorithm) - (offset afterOffset : - bindResult (readBytes 8 afterOffset) - (length afterLength : - bindResult (readBytes 32 afterLength) - (digest afterDigest : - ok - (pair sectionId - (pair sectionVersion - (pair sectionFlags - (pair compression - (pair digestAlgorithm - (pair offset - (pair length digest))))))) - afterDigest))))))))) - -readSectionDirectory_ = y (self bs sectionCount i acc : - matchBool - (ok (reverse acc) bs) - (bindResult (readSectionRecord bs) - (sectionRecord afterSectionRecord : - self afterSectionRecord sectionCount (succ i) (pair sectionRecord acc))) - (equal? i sectionCount)) - -readSectionDirectory = (sectionCount bs : readSectionDirectory_ bs sectionCount 0 t) - -sectionRecordId = (sectionRecord : - matchPair - (sectionId _ : sectionId) - sectionRecord) - -sectionRecordVersion = (sectionRecord : - matchPair - (_ payload : - matchPair - (sectionVersion _ : sectionVersion) - payload) - sectionRecord) - -sectionRecordFlags = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (sectionFlags _ : sectionFlags) - payload2) - payload) - sectionRecord) - -sectionRecordCompression = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (compression _ : compression) - payload3) - payload2) - payload) - sectionRecord) - -sectionRecordDigestAlgorithm = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (_ payload4 : - matchPair - (digestAlgorithm _ : digestAlgorithm) - payload4) - payload3) - payload2) - payload) - sectionRecord) - -sectionRecordOffset = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (_ payload4 : - matchPair - (_ payload5 : - matchPair - (offset _ : offset) - payload5) - payload4) - payload3) - payload2) - payload) - sectionRecord) - -sectionRecordLength = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (_ payload4 : - matchPair - (_ payload5 : - matchPair - (_ payload6 : - matchPair - (length _ : length) - payload6) - payload5) - payload4) - payload3) - payload2) - payload) - sectionRecord) - -sectionRecordDigest = (sectionRecord : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (_ payload4 : - matchPair - (_ payload5 : - matchPair - (_ payload6 : - matchPair - (_ digest : digest) - payload6) - payload5) - payload4) - payload3) - payload2) - payload) - sectionRecord) - -lookupSectionRecord_ = y (self directory sectionId : - matchList - nothing - (sectionRecord rest : - matchBool - (just sectionRecord) - (self rest sectionId) - (bytesEq? sectionId (sectionRecordId sectionRecord))) - directory) - -lookupSectionRecord = (sectionId directory : lookupSectionRecord_ directory sectionId) - -sectionDirectoryHasId?_ = y (self directory sectionId : - matchList - false - (sectionRecord rest : - or? - (bytesEq? sectionId (sectionRecordId sectionRecord)) - (self rest sectionId)) - directory) - -sectionDirectoryHasId? = (sectionId directory : sectionDirectoryHasId?_ directory sectionId) - -sectionDirectoryHasDuplicateIds? = y (self directory : - matchList - false - (sectionRecord rest : - or? - (sectionDirectoryHasId?_ rest (sectionRecordId sectionRecord)) - (self rest)) - directory) - -validateSectionDirectory = (directory rest : - matchBool - (err errDuplicateSection rest) - (ok directory rest) - (sectionDirectoryHasDuplicateIds? directory)) - -byteSlice = (offset length bytes : bytesTake length (bytesDrop offset bytes)) - -natMake = (bit rest : - matchBool - 0 - (pair bit rest) - (and? (equal? bit 0) (equal? rest 0))) - -natAdd = y (self a b : - triage - b - (_ : b) - (aBit aRest : - triage - a - (_ : a) - (bBit bRest : - matchBool - (natMake 0 (succ (self aRest bRest))) - (natMake (matchBool (matchBool 0 1 bBit) (matchBool 1 0 bBit) aBit) - (self aRest bRest)) - (and? (equal? aBit 1) (equal? bBit 1))) - b) - a) - -natDouble = (n : matchBool 0 (pair 0 n) (equal? n 0)) - -natTimes256 = (n : - natDouble - (natDouble - (natDouble - (natDouble - (natDouble - (natDouble - (natDouble - (natDouble n)))))))) - -byteNatShiftAppend_ = y (self byte acc i : - matchBool - acc - (triage - (natMake 0 (self 0 acc (succ i))) - (_ : acc) - (bit rest : natMake bit (self rest acc (succ i))) - byte) - (equal? i 8)) - -byteNatShiftAppend = (byte acc : byteNatShiftAppend_ byte acc 0) - -beBytesToNat = (bytes : - foldl - (acc byte : byteNatShiftAppend byte acc) - 0 - bytes) - -u32BEBytesToNat = beBytesToNat -u64BEBytesToNat = beBytesToNat - -arborixHeaderMajorVersion = (header : - matchPair - (majorVersion _ : majorVersion) - header) - -arborixHeaderMinorVersion = (header : - matchPair - (_ payload : - matchPair - (minorVersion _ : minorVersion) - payload) - header) - -arborixHeaderSectionCount = (header : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (sectionCount _ : sectionCount) - payload2) - payload) - header) - -arborixHeaderFlags = (header : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (flags _ : flags) - payload3) - payload2) - payload) - header) - -arborixHeaderDirOffset = (header : - matchPair - (_ payload : - matchPair - (_ payload2 : - matchPair - (_ payload3 : - matchPair - (_ dirOffset : dirOffset) - payload3) - payload2) - payload) - header) - -validateArborixHeader = (header rest : - matchBool - (ok header rest) - (err errUnsupportedVersion rest) - (and? - (bytesEq? arborixMajorVersion (arborixHeaderMajorVersion header)) - (bytesEq? arborixMinorVersion (arborixHeaderMinorVersion header)))) - -readArborixContainer = (bs : - bindResult (readArborixHeader bs) - (header afterHeader : - bindResult (validateArborixHeader header afterHeader) - (validHeader afterValidHeader : - bindResult (readSectionDirectory - (u32BEBytesToNat (arborixHeaderSectionCount validHeader)) - (bytesDrop (u64BEBytesToNat (arborixHeaderDirOffset validHeader)) bs)) - (directory afterDirectory : - bindResult (validateSectionDirectory directory afterDirectory) - (validDirectory afterValidDirectory : - ok (pair validHeader validDirectory) afterValidDirectory))))) - -sectionRecordOffsetNat = (sectionRecord : - u64BEBytesToNat (sectionRecordOffset sectionRecord)) - -sectionRecordLengthNat = (sectionRecord : - u64BEBytesToNat (sectionRecordLength sectionRecord)) - -extractSectionBytes = (sectionRecord containerBytes : - byteSlice - (sectionRecordOffsetNat sectionRecord) - (sectionRecordLengthNat sectionRecord) - containerBytes) - -extractSectionBytesResult = (sectionRecord containerBytes rest : - (sectionBytes : - matchBool - (ok sectionBytes rest) - (err errUnexpectedEof rest) - (equal? (bytesLength sectionBytes) (sectionRecordLengthNat sectionRecord))) - (extractSectionBytes sectionRecord containerBytes)) - -lookupSectionBytes = (sectionId directory containerBytes : - triage - nothing - (sectionRecord : just (extractSectionBytes sectionRecord containerBytes)) - (_ _ : nothing) - (lookupSectionRecord sectionId directory)) - -sectionBytesOrErr = (sectionId directory containerBytes rest : - triage - (err errMissingSection rest) - (sectionRecord : extractSectionBytesResult sectionRecord containerBytes rest) - (_ _ : err errMissingSection rest) - (lookupSectionRecord sectionId directory)) - -readArborixSectionBytes = (sectionId bs : - bindResult (readArborixContainer bs) - (container afterContainer : - matchPair - (_ directory : sectionBytesOrErr sectionId directory bs afterContainer) - container)) - -readArborixRequiredSections = (bs : - bindResult (readArborixContainer bs) - (container afterContainer : - matchPair - (_ directory : - bindResult (sectionBytesOrErr arborixManifestSectionId directory bs afterContainer) - (manifestBytes _ : - bindResult (sectionBytesOrErr arborixNodesSectionId directory bs afterContainer) - (nodesBytes _ : - ok (pair manifestBytes nodesBytes) afterContainer))) - container)) - -readNodeRecord = (bs : - bindResult (readBytes 32 bs) - (nodeHash afterNodeHash : - bindResult (readBytes 4 afterNodeHash) - (payloadLength afterPayloadLength : - bindResult (readBytes (u32BEBytesToNat payloadLength) afterPayloadLength) - (payload afterPayload : - ok - (pair nodeHash - (pair payloadLength payload)) - afterPayload)))) - -nodeRecordHash = (nodeRecord : - matchPair - (nodeHash _ : nodeHash) - nodeRecord) - -nodeRecordPayloadLength = (nodeRecord : - matchPair - (_ payload : - matchPair - (payloadLength _ : payloadLength) - payload) - nodeRecord) - -nodeRecordPayload = (nodeRecord : - matchPair - (_ payload : - matchPair - (_ nodePayload : nodePayload) - payload) - nodeRecord) - -nodePayloadKind = (nodePayload : bytesHead nodePayload) - -nodePayloadHasTag? = (tag nodePayload : - triage - false - (actualTag : byteEq? actualTag tag) - (_ _ : false) - (nodePayloadKind nodePayload)) - -nodePayloadLeaf? = (nodePayload : bytesEq? [(0)] nodePayload) - -nodePayloadStem? = (nodePayload : - and? - (nodePayloadHasTag? nodePayloadStemTag nodePayload) - (equal? (bytesLength nodePayload) 33)) - -nodePayloadFork? = (nodePayload : - and? - (nodePayloadHasTag? nodePayloadForkTag nodePayload) - (equal? (bytesLength nodePayload) 65)) - -nodePayloadValid? = (nodePayload : - or? - (nodePayloadLeaf? nodePayload) - (or? - (nodePayloadStem? nodePayload) - (nodePayloadFork? nodePayload))) - -nodePayloadStemChildHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload)) -nodePayloadForkLeftHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload)) -nodePayloadForkRightHash = (nodePayload : bytesTake 32 (bytesDrop 33 nodePayload)) - -nodeRecordPayloadValid? = (nodeRecord : nodePayloadValid? (nodeRecordPayload nodeRecord)) - -nodeRecordsHaveInvalidPayload? = y (self nodeRecords : - matchList - false - (nodeRecord rest : - or? - (not? (nodeRecordPayloadValid? nodeRecord)) - (self rest)) - nodeRecords) - -nodeRecordsHaveHash? = y (self nodeRecords nodeHash : - matchList - false - (nodeRecord rest : - or? - (bytesEq? nodeHash (nodeRecordHash nodeRecord)) - (self rest nodeHash)) - nodeRecords) - -nodeRecordsHaveDuplicateHashes? = y (self nodeRecords : - matchList - false - (nodeRecord rest : - or? - (nodeRecordsHaveHash? rest (nodeRecordHash nodeRecord)) - (self rest)) - nodeRecords) - -lookupNodeRecord_ = y (self nodeRecords nodeHash : - matchList - nothing - (nodeRecord rest : - matchBool - (just nodeRecord) - (self rest nodeHash) - (bytesEq? nodeHash (nodeRecordHash nodeRecord))) - nodeRecords) - -lookupNodeRecord = (nodeHash nodeRecords : lookupNodeRecord_ nodeRecords nodeHash) - -nodeRecordChildHashes = (nodeRecord : - (nodePayload : - matchBool - t - (matchBool - (pair (nodePayloadStemChildHash nodePayload) t) - (pair (nodePayloadForkLeftHash nodePayload) - (pair (nodePayloadForkRightHash nodePayload) t)) - (nodePayloadStem? nodePayload)) - (nodePayloadLeaf? nodePayload)) - (nodeRecordPayload nodeRecord)) - -nodeHashPresent? = (nodeHash nodeRecords : nodeRecordsHaveHash? nodeRecords nodeHash) - -nodeChildHashesPresent? = y (self childHashes nodeRecords : - matchList - true - (childHash rest : - and? - (nodeHashPresent? childHash nodeRecords) - (self rest nodeRecords)) - childHashes) - -nodeRecordChildrenPresent? = (nodeRecord nodeRecords : - nodeChildHashesPresent? (nodeRecordChildHashes nodeRecord) nodeRecords) - -nodeRecordsClosed? = y (self nodeRecords allNodeRecords : - matchList - true - (nodeRecord rest : - and? - (nodeRecordChildrenPresent? nodeRecord allNodeRecords) - (self rest allNodeRecords)) - nodeRecords) - -validateNodeRecords = (nodeRecords rest : - matchBool - (err errInvalidNodePayload rest) - (matchBool - (err errDuplicateNode rest) - (matchBool - (ok nodeRecords rest) - (err errMissingNode rest) - (nodeRecordsClosed? nodeRecords nodeRecords)) - (nodeRecordsHaveDuplicateHashes? nodeRecords)) - (nodeRecordsHaveInvalidPayload? nodeRecords)) - -readNodeRecords_ = y (self bs nodeCount i acc : - matchBool - (ok (reverse acc) bs) - (bindResult (readNodeRecord bs) - (nodeRecord afterNodeRecord : - self afterNodeRecord nodeCount (succ i) (pair nodeRecord acc))) - (equal? i nodeCount)) - -readNodeRecords = (nodeCount bs : readNodeRecords_ bs nodeCount 0 t) - -readNodesSection = (bs : - bindResult (readBytes 8 bs) - (nodeCount afterNodeCount : - bindResult (readNodeRecords (u64BEBytesToNat nodeCount) afterNodeCount) - (nodeRecords afterNodeRecords : - bindResult (validateNodeRecords nodeRecords afterNodeRecords) - (validNodeRecords afterValidNodeRecords : - ok (pair nodeCount validNodeRecords) afterValidNodeRecords)))) - -readNodesSectionComplete = (bs : - bindResult (readNodesSection bs) - (nodesSection afterNodesSection : - matchBool - (ok nodesSection afterNodesSection) - (err errUnexpectedBytes afterNodesSection) - (bytesNil? afterNodesSection))) - -readArborixNodesSection = (bs : - bindResult (readArborixContainer bs) - (container afterContainer : - matchPair - (_ directory : - bindResult (sectionBytesOrErr arborixNodesSectionId directory bs afterContainer) - (nodesBytes _ : - bindResult (readNodesSectionComplete nodesBytes) - (nodesSection _ : ok nodesSection afterContainer))) - container)) - -nodesSectionCount = (nodesSection : - matchPair - (nodeCount _ : nodeCount) - nodesSection) - -nodesSectionRecords = (nodesSection : - matchPair - (_ nodeRecords : nodeRecords) - nodesSection) - -nodeRecordToTreeWith = (self nodeRecords nodeRecord : - (nodePayload : - matchBool - (ok t t) - (matchBool - (bindResult (self (nodePayloadStemChildHash nodePayload) nodeRecords) - (child _ : ok (t child) t)) - (bindResult (self (nodePayloadForkLeftHash nodePayload) nodeRecords) - (left _ : - bindResult (self (nodePayloadForkRightHash nodePayload) nodeRecords) - (right _ : ok (pair left right) t))) - (nodePayloadStem? nodePayload)) - (nodePayloadLeaf? nodePayload)) - (nodeRecordPayload nodeRecord)) - -nodeHashToTree = y (self nodeHash nodeRecords : - triage - (err errMissingNode t) - (nodeRecord : nodeRecordToTreeWith self nodeRecords nodeRecord) - (_ _ : err errMissingNode t) - (lookupNodeRecord nodeHash nodeRecords)) - -readArborixTreeFromHash = (rootHash bs : - bindResult (readArborixNodesSection bs) - (nodesSection afterContainer : - bindResult (nodeHashToTree rootHash (nodesSectionRecords nodesSection)) - (tree _ : ok tree afterContainer))) - -readArborixExecutableFromHash = readArborixTreeFromHash diff --git a/src/FileEval.hs b/src/FileEval.hs @@ -162,7 +162,7 @@ nsVariable :: String -> String -> String nsVariable "" name = name nsVariable moduleName name = moduleName ++ "." ++ name --- | Compile a tricu source file to a standalone Arborix bundle. +-- | Compile a tricu source file to a standalone Arboricx bundle. -- Uses a temp content store so it does not collide with the global one. -- Supports multiple named exports; each is stored separately in the -- temp store so that resolveExportTarget can look them up by name. diff --git a/src/Main.hs b/src/Main.hs @@ -97,7 +97,7 @@ compileMode = Compile , names = def &= help "Definition name(s) to export as bundle roots (comma-separated or repeated -x). Defaults to 'main'." &= name "x" &= typ "NAME" } - &= help "Compile a tricu source file into a standalone Arborix portable bundle." + &= help "Compile a tricu source file into a standalone Arboricx portable bundle." &= explicit &= name "compile" @@ -106,7 +106,7 @@ serveMode = Serve { host = "127.0.0.1" &= help "Host to bind the server to." &= name "h" &= typ "HOST" , port = 8787 &= help "HTTP port to listen on." &= name "p" &= typ "PORT" } - &= help "Start a read-only HTTP server for exporting Arborix bundles." + &= help "Start a read-only HTTP server for exporting Arboricx bundles." &= explicit &= name "server" @@ -182,10 +182,10 @@ main = do let exportNames = if null namesArg then [] else map T.pack namesArg in compileFile compileInputFile compileOutFile exportNames Serve { host = hostStr, port = portNum } -> do - putStrLn $ "Starting Arborix bundle server on " ++ hostStr ++ ":" ++ show portNum + putStrLn $ "Starting Arboricx bundle server on " ++ hostStr ++ ":" ++ show portNum putStrLn $ " GET /bundle/hash/:hash -- primary endpoint" putStrLn $ " GET /bundle/name/:name -- convenience endpoint" - putStrLn $ " Content-Type: application/vnd.arborix.bundle" + putStrLn $ " Content-Type: application/vnd.arboricx.bundle" runServer hostStr portNum runTricu :: String -> String diff --git a/src/Research.hs b/src/Research.hs @@ -85,12 +85,12 @@ serializeNode (NFork l r) = BS.pack [0x02] <> go (decode (encodeUtf8 l)) <> go ( go (Right bs) = bs -- | Hash a node per the Merkle content-addressing spec. --- hash = SHA256( "arborix.merkle.node.v1" <> 0x00 <> node_payload ) +-- hash = SHA256( "arboricx.merkle.node.v1" <> 0x00 <> node_payload ) nodeHash :: Node -> MerkleHash nodeHash node = decodeUtf8 (encode (sha256WithPrefix (serializeNode node))) where sha256WithPrefix payload = convert . (hash :: BS.ByteString -> Digest SHA256) $ utf8Tag <> BS.pack [0x00] <> payload - utf8Tag = BS.pack $ map fromIntegral $ BS.unpack "arborix.merkle.node.v1" + utf8Tag = BS.pack $ map fromIntegral $ BS.unpack "arboricx.merkle.node.v1" -- | Deserialize a Node from canonical bytes. deserializeNode :: BS.ByteString -> Node @@ -138,7 +138,7 @@ toBytes t = case toList t of Left err -> Left err Right bs -> BS.pack <$> mapM toByte bs --- | Convert a canonical Arborix node payload (ByteString) to a Tree +-- | Convert a canonical Arboricx node payload (ByteString) to a Tree -- representation (a list of Byte trees). nodePayloadToTreeBytes :: BS.ByteString -> T nodePayloadToTreeBytes = ofBytes diff --git a/src/Server.hs b/src/Server.hs @@ -23,7 +23,7 @@ import Data.ByteString.Char8 (unpack) import Data.ByteString.Lazy (fromStrict) import qualified Data.Text as T --- | Start an HTTP server that serves Arborix bundles from the +-- | Start an HTTP server that serves Arboricx bundles from the -- local content store. -- -- This is a read-only export surface. Clients fetch bundle bytes @@ -133,10 +133,10 @@ rootsHandler request respond = do (fromStrict bundleData) -- | GET /bundle/name/:name --- Resolve a stored term name, export it as an Arborix bundle, +-- Resolve a stored term name, export it as an Arboricx bundle, -- and return the raw bundle bytes. -- --- Sets @Content-Type@ and @X-Arborix-Root-Hash@ headers. +-- Sets @Content-Type@ and @X-Arboricx-Root-Hash@ headers. -- Returns 404 when the name does not resolve to any stored term. nameHandler :: Text -> IO Response nameHandler nameText = do @@ -155,7 +155,7 @@ nameHandler nameText = do return $ responseLBS status200 (bundleHeaders th cd) (fromStrict bundleData) -- | GET /bundle/hash/:hash --- Resolve a full Merkle hash and export the root as an Arborix +-- Resolve a full Merkle hash and export the root as an Arboricx -- bundle. -- -- - Malformed hash (non-hex or < 16 chars): 400 @@ -207,8 +207,8 @@ textResponse status body = bundleHeaders :: Text -> Text -> [Header] bundleHeaders root cd = - [ (hContentType, encodeUtf8 "application/vnd.arborix.bundle") - , ("X-Arborix-Root-Hash", encodeUtf8 root) + [ (hContentType, encodeUtf8 "application/vnd.arboricx.bundle") + , ("X-Arboricx-Root-Hash", encodeUtf8 root) , ("Content-Disposition", encodeUtf8 cd) ] diff --git a/src/Wire.hs b/src/Wire.hs @@ -71,7 +71,7 @@ bundleMinorVersion = 0 -- | Header magic for the portable executable-object container. bundleMagic :: ByteString -bundleMagic = BS.pack [0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x58, 0x00] -- "ARBORIX\0" +bundleMagic = BS.pack [0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x43, 0x58] -- "ARBORICX" headerLength :: Int headerLength = 32 @@ -563,20 +563,20 @@ decodeSectionEntries count bytes = reverse <$> go count bytes [] defaultManifest :: [(Text, MerkleHash)] -> BundleManifest defaultManifest namedRoots = BundleManifest - { manifestSchema = "arborix.bundle.manifest.v1" + { manifestSchema = "arboricx.bundle.manifest.v1" , manifestBundleType = "tree-calculus-executable-object" , manifestTree = TreeSpec { treeCalculus = "tree-calculus.v1" , treeNodeHash = NodeHashSpec { nodeHashAlgorithm = "sha256" - , nodeHashDomain = "arborix.merkle.node.v1" + , nodeHashDomain = "arboricx.merkle.node.v1" } - , treeNodePayload = "arborix.merkle.payload.v1" + , treeNodePayload = "arboricx.merkle.payload.v1" } , manifestRuntime = RuntimeSpec { runtimeSemantics = "tree-calculus.v1" , runtimeEvaluation = "normal-order" - , runtimeAbi = "arborix.abi.tree.v1" + , runtimeAbi = "arboricx.abi.tree.v1" , runtimeCapabilities = [] } , manifestClosure = ClosureComplete @@ -587,7 +587,7 @@ defaultManifest namedRoots = BundleManifest , metadataVersion = Nothing , metadataDescription = Nothing , metadataLicense = Nothing - , metadataCreatedBy = Just "arborix" + , metadataCreatedBy = Just "arboricx" } } where @@ -597,7 +597,7 @@ defaultManifest namedRoots = BundleManifest { exportName = name , exportRoot = h , exportKind = "term" - , exportAbi = "arborix.abi.tree.v1" + , exportAbi = "arboricx.abi.tree.v1" } -- --------------------------------------------------------------------------- @@ -672,7 +672,7 @@ verifyBundle bundle = do verifyManifest :: BundleManifest -> Either String () verifyManifest manifest = do - when (manifestSchema manifest /= "arborix.bundle.manifest.v1") $ + when (manifestSchema manifest /= "arboricx.bundle.manifest.v1") $ Left $ "unsupported manifest schema: " ++ unpack (manifestSchema manifest) when (manifestBundleType manifest /= "tree-calculus-executable-object") $ Left $ "unsupported bundle type: " ++ unpack (manifestBundleType manifest) @@ -683,13 +683,13 @@ verifyManifest manifest = do Left $ "unsupported calculus: " ++ unpack (treeCalculus treeSpec) when (nodeHashAlgorithm hashSpec /= "sha256") $ Left $ "unsupported node hash algorithm: " ++ unpack (nodeHashAlgorithm hashSpec) - when (nodeHashDomain hashSpec /= "arborix.merkle.node.v1") $ + when (nodeHashDomain hashSpec /= "arboricx.merkle.node.v1") $ Left $ "unsupported node hash domain: " ++ unpack (nodeHashDomain hashSpec) - when (treeNodePayload treeSpec /= "arborix.merkle.payload.v1") $ + when (treeNodePayload treeSpec /= "arboricx.merkle.payload.v1") $ Left $ "unsupported node payload: " ++ unpack (treeNodePayload treeSpec) when (runtimeSemantics runtimeSpec /= "tree-calculus.v1") $ Left $ "unsupported runtime semantics: " ++ unpack (runtimeSemantics runtimeSpec) - when (runtimeAbi runtimeSpec /= "arborix.abi.tree.v1") $ + when (runtimeAbi runtimeSpec /= "arboricx.abi.tree.v1") $ Left $ "unsupported runtime ABI: " ++ unpack (runtimeAbi runtimeSpec) when (not (null (runtimeCapabilities runtimeSpec))) $ Left "unsupported runtime capabilities" diff --git a/test/Spec.hs b/test/Spec.hs @@ -786,7 +786,7 @@ wireTests = testGroup "Wire Tests" , "main = id t" ] wireData <- exportBundle srcConn [termHash] - BS.take 8 wireData @?= BS.pack [0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x58, 0x00] + BS.take 8 wireData @?= BS.pack [0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x43, 0x58] case decodeBundle wireData of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> do @@ -794,15 +794,15 @@ wireTests = testGroup "Wire Tests" tree = manifestTree manifest hashSpec = treeNodeHash tree runtime = manifestRuntime manifest - manifestSchema manifest @?= "arborix.bundle.manifest.v1" + manifestSchema manifest @?= "arboricx.bundle.manifest.v1" manifestBundleType manifest @?= "tree-calculus-executable-object" manifestClosure manifest @?= ClosureComplete treeCalculus tree @?= "tree-calculus.v1" - treeNodePayload tree @?= "arborix.merkle.payload.v1" + treeNodePayload tree @?= "arboricx.merkle.payload.v1" nodeHashAlgorithm hashSpec @?= "sha256" - nodeHashDomain hashSpec @?= "arborix.merkle.node.v1" + nodeHashDomain hashSpec @?= "arboricx.merkle.node.v1" runtimeSemantics runtime @?= "tree-calculus.v1" - runtimeAbi runtime @?= "arborix.abi.tree.v1" + runtimeAbi runtime @?= "arboricx.abi.tree.v1" runtimeCapabilities runtime @?= [] bundleRoots bundle @?= [termHash] map exportRoot (manifestExports manifest) @?= [termHash] @@ -823,7 +823,7 @@ wireTests = testGroup "Wire Tests" exportName exported @?= "validateEmail" exportRoot exported @?= termHash exportKind exported @?= "term" - exportAbi exported @?= "arborix.abi.tree.v1" + exportAbi exported @?= "arboricx.abi.tree.v1" exports -> assertFailure $ "Expected one export, got: " ++ show exports close srcConn @@ -1064,9 +1064,9 @@ u32 n = [0,0,0,n] u64 :: Integer -> [Integer] u64 n = [0,0,0,0,0,0,0,n] -arborixHeaderBytes :: Integer -> [Integer] -arborixHeaderBytes sectionCount = - [65,82,66,79,82,73,88,0] +arboricxHeaderBytes :: Integer -> [Integer] +arboricxHeaderBytes sectionCount = + [65,82,66,79,82,73,67,88] ++ u16 1 ++ u16 0 ++ u32 sectionCount @@ -1107,7 +1107,7 @@ simpleContainerBytes :: [Integer] -> [Integer] -> [Integer] simpleContainerBytes manifestBytes nodesBytes = let manifestOffset = 152 nodesOffset = manifestOffset + fromIntegral (length manifestBytes) - in arborixHeaderBytes 2 + in arboricxHeaderBytes 2 ++ manifestEntryBytes manifestOffset (fromIntegral $ length manifestBytes) ++ nodesEntryBytes nodesOffset (fromIntegral $ length nodesBytes) ++ manifestBytes @@ -1115,12 +1115,12 @@ simpleContainerBytes manifestBytes nodesBytes = singleSectionContainerBytes :: [Integer] -> [Integer] -> [Integer] singleSectionContainerBytes sectionType sectionBytes = - arborixHeaderBytes 1 + arboricxHeaderBytes 1 ++ sectionEntryBytes sectionType 92 (fromIntegral $ length sectionBytes) ++ sectionBytes -arborixHeaderT :: Integer -> T -arborixHeaderT sectionCount = +arboricxHeaderT :: Integer -> T +arboricxHeaderT sectionCount = pairT (bytesT [0,1]) (pairT (bytesT [0,0]) (pairT (bytesT $ u32 sectionCount) @@ -1615,120 +1615,114 @@ binaryReaderTests = testGroup "Binary Reader Tests" result env @?= errT eofT (bytesT [1,2,3]) -- ------------------------------------------------------------------------ - -- Arborix magic recognition + -- Arboricx magic recognition -- ------------------------------------------------------------------------ - , testCase "readArborixMagic: accepts magic and preserves rest" $ do - let input = "readArborixMagic [(65) (82) (66) (79) (82) (73) (88) (0) (1) (2)]" - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxMagic: accepts magic and preserves rest" $ do + let input = "readArboricxMagic ((append arboricxMagic) [(1) (2)])" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT unitT (bytesT [1,2]) - , testCase "readArborixMagic: rejects wrong magic preserving input" $ do - let input = "readArborixMagic [(65) (82) (66) (79) (82) (73) (88) (1) (9)]" - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxMagic: rejects wrong magic preserving input" $ do + let input = "readArboricxMagic [(65) (83) (66) (79) (82) (73) (67) (88) (1) (9)]" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) - result env @?= errT unexpectedBytesT (bytesT [65,82,66,79,82,73,88,1,9]) + result env @?= errT unexpectedBytesT (bytesT [65,83,66,79,82,73,67,88,1,9]) - , testCase "readArborixMagic: short input returns EOF preserving input" $ do - let input = "readArborixMagic [(65) (82) (66) (79)]" - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxMagic: short input returns EOF preserving input" $ do + let input = "readArboricxMagic [(65) (82) (66) (79)]" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [65,82,66,79]) -- ------------------------------------------------------------------------ - -- Arborix header parsing + -- Arboricx header parsing -- ------------------------------------------------------------------------ - , testCase "readArborixHeader: parses portable header" $ do - let input = "readArborixHeader " ++ bytesExpr (arborixHeaderBytes 0) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxHeader: parses portable header" $ do + let input = "readArboricxHeader " ++ bytesExpr (arboricxHeaderBytes 0) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) - result env @?= okT (arborixHeaderT 0) (bytesT []) + result env @?= okT (arboricxHeaderT 0) (bytesT []) - , testCase "readArborixHeader: preserves trailing bytes" $ do - let input = "readArborixHeader " ++ bytesExpr (arborixHeaderBytes 0 ++ [9,8]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxHeader: preserves trailing bytes" $ do + let input = "readArboricxHeader " ++ bytesExpr (arboricxHeaderBytes 0 ++ [9,8]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) - result env @?= okT (arborixHeaderT 0) (bytesT [9,8]) + result env @?= okT (arboricxHeaderT 0) (bytesT [9,8]) - , testCase "readArborixHeader: rejects wrong magic preserving input" $ do - let input = "readArborixHeader [(65) (82) (66) (79) (82) (73) (88) (1) (0) (1)]" - library <- evaluateFile "./lib/arborix.tri" - let env = evalTricu library (parseTricu input) - result env @?= errT unexpectedBytesT (bytesT [65,82,66,79,82,73,88,1,0,1]) - - , testCase "readArborixHeader: short input returns EOF preserving input" $ do - let input = "readArborixHeader [(65) (82)]" - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxHeader: short input returns EOF preserving input" $ do + let input = "readArboricxHeader [(65) (82)]" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [65,82]) -- ------------------------------------------------------------------------ - -- Arborix section directory record parsing + -- Arboricx section directory record parsing -- ------------------------------------------------------------------------ , testCase "readSectionRecord: parses portable section entry" $ do let input = "readSectionRecord " ++ bytesExpr (nodesEntryBytes 16 32) - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (sectionRecordT nodesSectionIdBytes 16 32) (bytesT []) , testCase "readSectionRecord: preserves trailing bytes" $ do let input = "readSectionRecord " ++ bytesExpr (nodesEntryBytes 16 32 ++ [9,8]) - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (sectionRecordT nodesSectionIdBytes 16 32) (bytesT [9,8]) , testCase "readSectionRecord: empty input returns EOF" $ do let input = "readSectionRecord []" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT []) , testCase "readSectionRecord: short section id returns EOF preserving input" $ do let input = "readSectionRecord [(0)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0]) , testCase "readSectionRecord: missing section version returns EOF preserving unread bytes" $ do let input = "readSectionRecord [(0) (2)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0,2]) , testCase "readSectionRecord: short section version returns EOF preserving unread bytes" $ do let input = "readSectionRecord [(0) (2) (0) (0) (0)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0]) , testCase "readSectionRecord: missing length returns EOF preserving unread length bytes" $ do let input = "readSectionRecord [(0) (2) (0) (0) (0) (16)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT []) , testCase "readSectionRecord: short section flags returns EOF preserving unread bytes" $ do let input = "readSectionRecord [(0) (2) (0) (0) (0) (16) (0) (0) (0)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0]) -- ------------------------------------------------------------------------ - -- Arborix section directory parsing + -- Arboricx section directory parsing -- ------------------------------------------------------------------------ , testCase "readSectionDirectory: zero records preserves input" $ do let input = "readSectionDirectory 0 [(9) (8)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (ofList []) (bytesT [9,8]) , testCase "readSectionDirectory: reads requested records and preserves trailing bytes" $ do let input = "readSectionDirectory 2 " ++ bytesExpr (manifestEntryBytes 10 20 ++ nodesEntryBytes 30 40 ++ [9]) - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (ofList @@ -1739,171 +1733,171 @@ binaryReaderTests = testGroup "Binary Reader Tests" , testCase "readSectionDirectory: truncated record returns EOF" $ do let input = "readSectionDirectory 2 [(0) (1) (0) (0) (0) (10) (0) (0) (0) (20) (0) (2) (0) (0)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0,0]) -- ------------------------------------------------------------------------ - -- Arborix section lookup and raw byte slicing + -- Arboricx section lookup and raw byte slicing -- ------------------------------------------------------------------------ , testCase "lookupSectionRecord: finds record by raw section id" $ do let input = "lookupSectionRecord " ++ bytesExpr nodesSectionIdBytes ++ " [(" ++ "pair " ++ bytesExpr manifestSectionIdBytes ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,0] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr (u64 10) ++ " (pair " ++ bytesExpr (u64 20) ++ " " ++ bytesExpr (replicate 32 0) ++ "))))))" ++ ") (" ++ "pair " ++ bytesExpr nodesSectionIdBytes ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,0] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr (u64 30) ++ " (pair " ++ bytesExpr (u64 40) ++ " " ++ bytesExpr (replicate 32 0) ++ "))))))" ++ ")]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= justT (sectionRecordT nodesSectionIdBytes 30 40) , testCase "lookupSectionRecord: missing section id returns nothing" $ do let input = "lookupSectionRecord " ++ bytesExpr [0,0,0,3] ++ " [(" ++ "pair " ++ bytesExpr manifestSectionIdBytes ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,0] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr (u64 10) ++ " (pair " ++ bytesExpr (u64 20) ++ " " ++ bytesExpr (replicate 32 0) ++ "))))))" ++ ") (" ++ "pair " ++ bytesExpr nodesSectionIdBytes ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr [0,0] ++ " (pair " ++ bytesExpr [0,1] ++ " (pair " ++ bytesExpr (u64 30) ++ " (pair " ++ bytesExpr (u64 40) ++ " " ++ bytesExpr (replicate 32 0) ++ "))))))" ++ ")]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= nothingT , testCase "byteSlice: extracts requested byte range" $ do let input = "byteSlice 2 3 [(10) (11) (12) (13) (14) (15)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= bytesT [12,13,14] , testCase "byteSlice: overlong length returns remaining bytes" $ do let input = "byteSlice 4 9 [(10) (11) (12) (13) (14) (15)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= bytesT [14,15] -- ------------------------------------------------------------------------ - -- Arborix minimal container parsing foundation + -- Arboricx minimal container parsing foundation -- ------------------------------------------------------------------------ , testCase "u32BEBytesToNat: decodes zero" $ do let input = "u32BEBytesToNat [(0) (0) (0) (0)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 , testCase "u32BEBytesToNat: decodes small section count" $ do let input = "u32BEBytesToNat [(0) (0) (0) (2)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 2 , testCase "u64BEBytesToNat: decodes small node count" $ do let input = "u64BEBytesToNat [(0) (0) (0) (0) (0) (0) (0) (2)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 2 , testCase "u64BEBytesToNat: decodes fixture-scale offset" $ do let input = "u64BEBytesToNat [(0) (0) (0) (0) (0) (0) (3) (214)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 982 - , testCase "readArborixContainer: reads header directory and preserves payload" $ do - let input = "readArborixContainer " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxContainer: reads header directory and preserves payload" $ do + let input = "readArboricxContainer " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (pairT - (arborixHeaderT 2) + (arboricxHeaderT 2) (ofList [ sectionRecordT manifestSectionIdBytes 152 3 , sectionRecordT nodesSectionIdBytes 155 4 ])) (bytesT [101,102,103,201,202,203,204]) - , testCase "readArborixContainer: truncated directory returns EOF" $ do - let input = "readArborixContainer " ++ bytesExpr (arborixHeaderBytes 1 ++ [0,0]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxContainer: truncated directory returns EOF" $ do + let input = "readArboricxContainer " ++ bytesExpr (arboricxHeaderBytes 1 ++ [0,0]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [0,0]) - , testCase "readArborixContainer: rejects unsupported major version" $ do - let badHeader = [65,82,66,79,82,73,88,0] ++ u16 2 ++ u16 0 ++ u32 0 ++ u64 0 ++ u64 32 - input = "readArborixContainer " ++ bytesExpr badHeader - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxContainer: rejects unsupported major version" $ do + let badHeader = [65,82,66,79,82,73,67,88] ++ u16 2 ++ u16 0 ++ u32 0 ++ u64 0 ++ u64 32 + input = "readArboricxContainer " ++ bytesExpr badHeader + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT unsupportedVersionT (bytesT []) - , testCase "readArborixContainer: rejects unsupported minor version" $ do - let badHeader = [65,82,66,79,82,73,88,0] ++ u16 1 ++ u16 1 ++ u32 0 ++ u64 0 ++ u64 32 - input = "readArborixContainer " ++ bytesExpr badHeader - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxContainer: rejects unsupported minor version" $ do + let badHeader = [65,82,66,79,82,73,67,88] ++ u16 1 ++ u16 1 ++ u32 0 ++ u64 0 ++ u64 32 + input = "readArboricxContainer " ++ bytesExpr badHeader + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT unsupportedVersionT (bytesT []) - , testCase "readArborixContainer: rejects duplicate section ids" $ do - let input = "readArborixContainer " ++ bytesExpr (arborixHeaderBytes 2 ++ manifestEntryBytes 152 1 ++ manifestEntryBytes 153 1 ++ [9]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxContainer: rejects duplicate section ids" $ do + let input = "readArboricxContainer " ++ bytesExpr (arboricxHeaderBytes 2 ++ manifestEntryBytes 152 1 ++ manifestEntryBytes 153 1 ++ [9]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT duplicateSectionT (bytesT [9]) , testCase "extractSectionBytes: uses raw offset and length fields" $ do let input = "extractSectionBytes " ++ sectionRecordExpr nodesSectionIdBytes 3 4 ++ " " ++ bytesExpr [10,11,12,13,14,15,16,17] - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= bytesT [13,14,15,16] , testCase "lookupSectionBytes: finds section and extracts raw bytes" $ do let input = "lookupSectionBytes " ++ bytesExpr nodesSectionIdBytes ++ " [" ++ sectionRecordExpr manifestSectionIdBytes 1 2 ++ " " ++ sectionRecordExpr nodesSectionIdBytes 4 3 ++ "] " ++ bytesExpr [10,11,12,13,14,15,16,17] - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= justT (bytesT [14,15,16]) , testCase "lookupSectionBytes: missing section returns nothing" $ do let input = "lookupSectionBytes " ++ bytesExpr [0,0,0,3] ++ " [" ++ sectionRecordExpr manifestSectionIdBytes 1 2 ++ " " ++ sectionRecordExpr nodesSectionIdBytes 4 3 ++ "] " ++ bytesExpr [10,11,12,13,14,15,16,17] - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= nothingT , testCase "extractSectionBytesResult: rejects out-of-bounds section" $ do let input = "extractSectionBytesResult " ++ sectionRecordExpr nodesSectionIdBytes 6 4 ++ " " ++ bytesExpr [10,11,12,13,14,15,16,17] ++ " []" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT []) - , testCase "readArborixSectionBytes: extracts requested section from container" $ do - let input = "readArborixSectionBytes " ++ bytesExpr nodesSectionIdBytes ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxSectionBytes: extracts requested section from container" $ do + let input = "readArboricxSectionBytes " ++ bytesExpr nodesSectionIdBytes ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (bytesT [201,202,203,204]) (bytesT [101,102,103,201,202,203,204]) - , testCase "readArborixSectionBytes: missing section returns missing-section err" $ do - let input = "readArborixSectionBytes " ++ bytesExpr nodesSectionIdBytes ++ " " ++ bytesExpr (singleSectionContainerBytes manifestSectionIdBytes [101,102,103]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxSectionBytes: missing section returns missing-section err" $ do + let input = "readArboricxSectionBytes " ++ bytesExpr nodesSectionIdBytes ++ " " ++ bytesExpr (singleSectionContainerBytes manifestSectionIdBytes [101,102,103]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT missingSectionT (bytesT [101,102,103]) - , testCase "readArborixRequiredSections: extracts manifest and nodes bytes" $ do - let input = "readArborixRequiredSections " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxRequiredSections: extracts manifest and nodes bytes" $ do + let input = "readArboricxRequiredSections " ++ bytesExpr (simpleContainerBytes [101,102,103] [201,202,203,204]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (pairT (bytesT [101,102,103]) (bytesT [201,202,203,204])) (bytesT [101,102,103,201,202,203,204]) - , testCase "readArborixRequiredSections: missing nodes section returns missing-section err" $ do - let input = "readArborixRequiredSections " ++ bytesExpr (singleSectionContainerBytes manifestSectionIdBytes [101,102,103]) - library <- evaluateFile "./lib/arborix.tri" + , testCase "readArboricxRequiredSections: missing nodes section returns missing-section err" $ do + let input = "readArboricxRequiredSections " ++ bytesExpr (singleSectionContainerBytes manifestSectionIdBytes [101,102,103]) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT missingSectionT (bytesT [101,102,103]) - , testCase "readArborixRequiredSections: out-of-bounds section returns EOF" $ do + , testCase "readArboricxRequiredSections: out-of-bounds section returns EOF" $ do let manifestBytes = [101,102,103] nodesBytes = [201,202,203,204] - badContainer = arborixHeaderBytes 2 ++ manifestEntryBytes 152 3 ++ nodesEntryBytes 155 9 ++ manifestBytes ++ nodesBytes - input = "readArborixRequiredSections " ++ bytesExpr badContainer - library <- evaluateFile "./lib/arborix.tri" + badContainer = arboricxHeaderBytes 2 ++ manifestEntryBytes 152 3 ++ nodesEntryBytes 155 9 ++ manifestBytes ++ nodesBytes + input = "readArboricxRequiredSections " ++ bytesExpr badContainer + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [101,102,103,201,202,203,204]) -- ------------------------------------------------------------------------ - -- Arborix raw nodes section parsing + -- Arboricx raw nodes section parsing -- ------------------------------------------------------------------------ , testCase "readNodeRecord: parses hash length and raw payload" $ do let input = "readNodeRecord [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (3) (101) (102) (103) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (pairT (bytesT [1..32]) @@ -1913,13 +1907,13 @@ binaryReaderTests = testGroup "Binary Reader Tests" , testCase "readNodeRecord: truncated payload returns EOF preserving unread payload" $ do let input = "readNodeRecord [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (3) (101) (102)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT eofT (bytesT [101,102]) , testCase "readNodesSection: parses node count and records" $ do let input = "readNodesSection [(0) (0) (0) (0) (0) (0) (0) (1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (1) (0) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (pairT (bytesT [0,0,0,0,0,0,0,1]) @@ -1932,37 +1926,37 @@ binaryReaderTests = testGroup "Binary Reader Tests" , testCase "readNodesSectionComplete: rejects trailing bytes inside nodes section" $ do let input = "readNodesSectionComplete [(0) (0) (0) (0) (0) (0) (0) (0) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT unexpectedBytesT (bytesT [9]) , testCase "readNodesSection: rejects duplicate node hashes" $ do let input = "readNodesSection [(0) (0) (0) (0) (0) (0) (0) (2) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (1) (0) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (1) (0) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT duplicateNodeT (bytesT [9]) , testCase "nodePayloadValid?: accepts leaf stem and fork payload shapes" $ do let input = "[(nodePayloadValid? [(0)]) (nodePayloadValid? [(1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)]) (nodePayloadValid? [(2) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)])]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofList [trueT, trueT, trueT] , testCase "nodePayloadValid?: rejects invalid payload shapes" $ do let input = "[(nodePayloadValid? []) (nodePayloadValid? [(9)]) (nodePayloadValid? [(1) (1)]) (nodePayloadValid? [(2) (1) (2)])]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofList [falseT, falseT, falseT, falseT] , testCase "node payload child accessors expose raw hashes" $ do let input = "[(nodePayloadStemChildHash [(1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)]) (nodePayloadForkLeftHash [(2) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)]) (nodePayloadForkRightHash [(2) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)])]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofList [bytesT [1..32], bytesT [1..32], bytesT [33..64]] , testCase "lookupNodeRecord: finds record by raw node hash" $ do let input = "lookupNodeRecord [(33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)] [(pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (1)] [(0)])) (pair [(33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)] (pair [(0) (0) (0) (1)] [(0)]))]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= justT (pairT (bytesT [33..64]) @@ -1971,7 +1965,7 @@ binaryReaderTests = testGroup "Binary Reader Tests" , testCase "nodeRecordChildHashes: extracts stem and fork references" $ do let input = "[(nodeRecordChildHashes (pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (33)] [(1) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)]))) (nodeRecordChildHashes (pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (65)] [(2) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64) (65) (66) (67) (68) (69) (70) (71) (72) (73) (74) (75) (76) (77) (78) (79) (80) (81) (82) (83) (84) (85) (86) (87) (88) (89) (90) (91) (92) (93) (94) (95) (96)])))]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofList [ ofList [bytesT [33..64]] @@ -1980,20 +1974,20 @@ binaryReaderTests = testGroup "Binary Reader Tests" , testCase "readNodesSection: rejects invalid node payload shape" $ do let input = "readNodesSection [(0) (0) (0) (0) (0) (0) (0) (1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (1) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT invalidNodePayloadT (bytesT []) , testCase "readNodesSection: rejects missing child node" $ do let input = "readNodesSection [(0) (0) (0) (0) (0) (0) (0) (1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (0) (0) (0) (33) (1) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64) (9)]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= errT missingNodeT (bytesT [9]) - , testCase "readArborixNodesSection: extracts and parses raw nodes section" $ do + , testCase "readArboricxNodesSection: extracts and parses raw nodes section" $ do let nodesBytes = u64 1 ++ [1..32] ++ u32 1 ++ [0] - input = "readArborixNodesSection " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) - library <- evaluateFile "./lib/arborix.tri" + input = "readArboricxNodesSection " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (pairT (bytesT [0,0,0,0,0,0,0,1]) @@ -2005,186 +1999,186 @@ binaryReaderTests = testGroup "Binary Reader Tests" (bytesT ([101,102,103] ++ nodesBytes)) -- ------------------------------------------------------------------------ - -- Arborix node DAG reconstruction + -- Arboricx node DAG reconstruction -- ------------------------------------------------------------------------ , testCase "nodeHashToTree: reconstructs leaf node" $ do let input = "nodeHashToTree [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] [(pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (1)] [(0)]))]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT Leaf Leaf , testCase "nodeHashToTree: reconstructs stem node" $ do let input = "nodeHashToTree [(33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)] [(pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (1)] [(0)])) (pair [(33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)] (pair [(0) (0) (0) (33)] [(1) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)]))]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (Stem Leaf) Leaf , testCase "nodeHashToTree: reconstructs fork node" $ do let input = "nodeHashToTree [(65) (66) (67) (68) (69) (70) (71) (72) (73) (74) (75) (76) (77) (78) (79) (80) (81) (82) (83) (84) (85) (86) (87) (88) (89) (90) (91) (92) (93) (94) (95) (96)] [(pair [(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32)] (pair [(0) (0) (0) (1)] [(0)])) (pair [(33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)] (pair [(0) (0) (0) (1)] [(0)])) (pair [(65) (66) (67) (68) (69) (70) (71) (72) (73) (74) (75) (76) (77) (78) (79) (80) (81) (82) (83) (84) (85) (86) (87) (88) (89) (90) (91) (92) (93) (94) (95) (96)] (pair [(0) (0) (0) (65)] [(2) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31) (32) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (57) (58) (59) (60) (61) (62) (63) (64)]))]" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT (Fork Leaf Leaf) Leaf - , testCase "readArborixTreeFromHash: reconstructs tree from bundle bytes" $ do + , testCase "readArboricxTreeFromHash: reconstructs tree from bundle bytes" $ do let nodesBytes = u64 1 ++ [1..32] ++ u32 1 ++ [0] - input = "readArborixTreeFromHash " ++ bytesExpr [1..32] ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) - library <- evaluateFile "./lib/arborix.tri" + input = "readArboricxTreeFromHash " ++ bytesExpr [1..32] ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT Leaf (bytesT ([101,102,103] ++ nodesBytes)) - , testCase "readArborixExecutableFromHash: alias reconstructs tree" $ do + , testCase "readArboricxExecutableFromHash: alias reconstructs tree" $ do let nodesBytes = u64 1 ++ [1..32] ++ u32 1 ++ [0] - input = "readArborixExecutableFromHash " ++ bytesExpr [1..32] ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) - library <- evaluateFile "./lib/arborix.tri" + input = "readArboricxExecutableFromHash " ++ bytesExpr [1..32] ++ " " ++ bytesExpr (simpleContainerBytes [101,102,103] nodesBytes) + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= okT Leaf (bytesT ([101,102,103] ++ nodesBytes)) - , testCase "readArborixNodesSection: reads id fixture bundle" $ do - fixtureBytes <- BS.readFile "test/fixtures/id.arborix" + , testCase "readArboricxNodesSection: reads id fixture bundle" $ do + fixtureBytes <- BS.readFile "test/fixtures/id.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right _ -> do - let input = "matchResult (code rest : code) (nodes rest : 0) (readArborixNodesSection " + let input = "matchResult (code rest : code) (nodes rest : 0) (readArboricxNodesSection " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixNodesSection: reads notQ fixture bundle" $ do - fixtureBytes <- BS.readFile "test/fixtures/notQ.arborix" + , testCase "readArboricxNodesSection: reads notQ fixture bundle" $ do + fixtureBytes <- BS.readFile "test/fixtures/notQ.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right _ -> do - let input = "matchResult (code rest : code) (nodes rest : 0) (readArborixNodesSection " + let input = "matchResult (code rest : code) (nodes rest : 0) (readArboricxNodesSection " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixNodesSection: reads map fixture bundle" $ do - fixtureBytes <- BS.readFile "test/fixtures/map.arborix" + , testCase "readArboricxNodesSection: reads map fixture bundle" $ do + fixtureBytes <- BS.readFile "test/fixtures/map.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right _ -> do - let input = "matchResult (code rest : code) (nodes rest : 0) (readArborixNodesSection " + let input = "matchResult (code rest : code) (nodes rest : 0) (readArboricxNodesSection " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixExecutableFromHash: reconstructs id fixture root" $ do - fixtureBytes <- BS.readFile "test/fixtures/id.arborix" + , testCase "readArboricxExecutableFromHash: reconstructs id fixture root" $ do + fixtureBytes <- BS.readFile "test/fixtures/id.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : 0) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : 0) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixExecutableFromHash: reconstructs notQ fixture root" $ do - fixtureBytes <- BS.readFile "test/fixtures/notQ.arborix" + , testCase "readArboricxExecutableFromHash: reconstructs notQ fixture root" $ do + fixtureBytes <- BS.readFile "test/fixtures/notQ.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : 0) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : 0) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixExecutableFromHash: reconstructs map fixture root" $ do - fixtureBytes <- BS.readFile "test/fixtures/map.arborix" + , testCase "readArboricxExecutableFromHash: reconstructs map fixture root" $ do + fixtureBytes <- BS.readFile "test/fixtures/map.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : 0) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : 0) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 0 - , testCase "readArborixExecutableFromHash: executes id fixture root" $ do - fixtureBytes <- BS.readFile "test/fixtures/id.arborix" + , testCase "readArboricxExecutableFromHash: executes id fixture root" $ do + fixtureBytes <- BS.readFile "test/fixtures/id.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : tree 42) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : tree 42) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= ofNumber 42 - , testCase "readArborixExecutableFromHash: executes notQ fixture on true" $ do - fixtureBytes <- BS.readFile "test/fixtures/notQ.arborix" + , testCase "readArboricxExecutableFromHash: executes notQ fixture on true" $ do + fixtureBytes <- BS.readFile "test/fixtures/notQ.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : tree true) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : tree true) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= falseT - , testCase "readArborixExecutableFromHash: executes notQ fixture on false" $ do - fixtureBytes <- BS.readFile "test/fixtures/notQ.arborix" + , testCase "readArboricxExecutableFromHash: executes notQ fixture on false" $ do + fixtureBytes <- BS.readFile "test/fixtures/notQ.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : tree false) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : tree false) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= trueT - , testCase "readArborixExecutableFromHash: executes map fixture root" $ do - fixtureBytes <- BS.readFile "test/fixtures/map.arborix" + , testCase "readArboricxExecutableFromHash: executes map fixture root" $ do + fixtureBytes <- BS.readFile "test/fixtures/map.arboricx" case decodeBundle fixtureBytes of Left err -> assertFailure $ "decodeBundle failed: " ++ err Right bundle -> case bundleRoots bundle of [] -> assertFailure "fixture has no roots" (rootHash:_) -> do - let input = "matchResult (code rest : code) (tree rest : head (tail (tree (a : (t t t)) [(t) (t) (t)]))) (readArborixExecutableFromHash " + let input = "matchResult (code rest : code) (tree rest : head (tail (tree (a : (t t t)) [(t) (t) (t)]))) (readArboricxExecutableFromHash " ++ bytesExpr (hexTextBytes rootHash) ++ " " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")" - library <- evaluateFile "./lib/arborix.tri" + library <- evaluateFile "./lib/arboricx.tri" let env = evalTricu library (parseTricu input) result env @?= Fork Leaf Leaf ] diff --git a/test/fixtures/false.arboricx b/test/fixtures/false.arboricx Binary files differ. diff --git a/test/fixtures/false.arborix b/test/fixtures/false.arborix Binary files differ. diff --git a/test/fixtures/id.arboricx b/test/fixtures/id.arboricx Binary files differ. diff --git a/test/fixtures/id.arborix b/test/fixtures/id.arborix Binary files differ. diff --git a/test/fixtures/map.arboricx b/test/fixtures/map.arboricx Binary files differ. diff --git a/test/fixtures/map.arborix b/test/fixtures/map.arborix Binary files differ. diff --git a/test/fixtures/notQ.arboricx b/test/fixtures/notQ.arboricx Binary files differ. diff --git a/test/fixtures/notQ.arborix b/test/fixtures/notQ.arborix Binary files differ. diff --git a/test/fixtures/true.arboricx b/test/fixtures/true.arboricx Binary files differ. diff --git a/test/fixtures/true.arborix b/test/fixtures/true.arborix Binary files differ.