-
Notifications
You must be signed in to change notification settings - Fork 23
Description
What was discovered
CBOR.ts has two complete decoder implementations side-by-side: an offset-based family (`decodeItemAt`, `decodeBytesAt`, `decodeArrayAt`, …) and a slice-based family (`decodeItemWithLengthSync`, `decodeBytesWithLengthSync`, `decodeArrayWithLengthSync`, …). The slice-based family calls `data.slice(offset)` at every recursion, allocating a new `Uint8Array` for each nested item.
Surfaced during
CBOR module review while preparing the `bounded_bytes` / `BoundedBytes` fix (PR for Conway CDDL compliance). Found during full read of CBOR.ts decoder section (~lines 1600–1900).
Why it's worth exploring
For deeply nested PlutusData (constr with 5 levels, many fields), the slice-based path allocates hundreds of `Uint8Array` copies. The offset-based family is strictly better — it threads an integer offset through the call chain with zero allocation overhead. The two families are functionally equivalent. Deleting the `WithLength` family and routing its callers to the offset-based one removes ~200 lines and eliminates the O(n²) allocation pattern.
What would be needed
- Identify all callers of the `WithLength` family inside CBOR.ts
- Route them to the offset-based equivalents (`decodeItemAt` etc.)
- Delete the `WithLength` family
- Run the full CBOR test suite (968 tests) to confirm no regression
- Benchmark with a deeply nested PlutusData value to quantify the win (optional but useful)
Context snapshot
Relevant section: `packages/evolution/src/CBOR.ts` ~lines 1600–1900.
The slice-based family:
```typescript
const decodeItemWithLengthSync = (data: Uint8Array, options: CodecOptions): { item: CBOR; bytesConsumed: number } => {
// ... dispatches to decodeBytesWithLengthSync, decodeArrayWithLengthSync, etc.
// Each calls data.slice(offset) before recursing — one allocation per node
}
```
The offset-based family (keeper):
```typescript
const decodeItemAt = (data: Uint8Array, offset: number, options: CodecOptions): DecodeAtResult => {
// ... threads offset integer — zero allocation
}
```
Both produce identical decoded values. The `WithLength` family exists as a legacy parallel path and has no unique capabilities the offset family lacks.