From b7fd7b41181d09947aa04e5d7b9741e3e8a5aa5f Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:36:57 -0800 Subject: [PATCH 1/3] chore(quest): claim quest/m3/ts-option-slot.md Quest-Claim: bdef938c748b0c1f From 4467228e87879b74bf1aa31659fecab69856ed60 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:48:32 -0800 Subject: [PATCH 2/3] perf(ts): return a Result of an Option of a number in a slot An `Option` of a number is one JS value, that or `undefined`, so a local holds it as one part rather than a flag and the number. A `Result` whose `Ok` is numbers alone, an `Option` of one among them, such as `Decoder::varint_opt`'s `Result, E>`, now hands back through the slot as `Result` does, with no `rt.Ok` per call. Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 1 - quest/m3/ts-option-slot.md | 17 - ts/MIGRATE.md | 5 +- ts/README.md | 7 +- .../codec/expected/coding/decode.Decoder.ts | 9 +- ts/fixtures/codec/expected/harness.ts | 49 +- ts/fixtures/codec/expected/lib.ts | 2 + ts/fixtures/lang/cells/expected/cells.ts | 33 +- ts/fixtures/lang/cells/expected/lib.ts | 2 + .../lang/returns/expected/core/bool.ts | 7 + .../lang/returns/expected/core/option.ts | 32 +- .../lang/returns/expected/core/result.ts | 12 +- ts/fixtures/lang/returns/expected/lib.ts | 40 +- .../lang/returns/expected/returns.Error.ts | 12 + .../lang/returns/expected/returns.Opted.ts | 26 + ts/fixtures/lang/returns/expected/returns.ts | 807 +++++++++++++++++- ts/fixtures/lang/returns/src/lib.rs | 2 +- ts/fixtures/lang/returns/src/returns.rs | 172 +++- ts/fixtures/lang/shapes/expected/lib.ts | 2 + ts/fixtures/lang/shapes/expected/shapes.ts | 15 +- .../lang/splits/expected/core/option.ts | 7 - ts/fixtures/lang/splits/expected/splits.ts | 9 +- ts/src/lower/mir/scalar.rs | 45 +- 23 files changed, 1208 insertions(+), 105 deletions(-) delete mode 100644 quest/m3/ts-option-slot.md create mode 100644 ts/fixtures/lang/returns/expected/returns.Opted.ts diff --git a/quest/m3/README.md b/quest/m3/README.md index 239ea51a2..bc7575964 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -38,7 +38,6 @@ generic std gap is planned here even when moq never calls it. - [Debug of a LazyCell or a LazyLock](/quest/m3/ts-lazy-debug.md) - `{:?}` of a `LazyCell` and a `LazyLock` writes its value or ``, as std's do - [Type errors at the Rust that made them](/quest/m3/ts-span-precision.md) - a `tsc` error on output names the tail expression or call argument that made it - [An empty iterator of mutable references](/quest/m3/ts-empty-places.md) - `iter::empty::<&mut T>()` given to `zip` or `chain` translates -- [An Option of numbers in the result slot](/quest/m3/ts-option-slot.md) - `varint_opt`'s `Option` comes back through the slot with no object per call - [Dropping a crate's generic type inside another](/quest/m3/ts-nested-generic-drop.md) - an `LruCache` holding a `LinkedHashMap` drops as rustc's glue does - [Fresh writes through a zip, a copy, and a repeat](/quest/m3/ts-fresh-shapes.md) - zipping a `Vec<[T; N]>`, reading a `T` out of `Zip::next`, and `copied` or `vec![x; n]` over a `Copy` type translate - [Python's wake on the loop's own thread](/quest/m3/ffi-python-wake.md) - a wake on the event loop's thread resumes the future with no socket pair diff --git a/quest/m3/ts-option-slot.md b/quest/m3/ts-option-slot.md deleted file mode 100644 index 8c51ebf72..000000000 --- a/quest/m3/ts-option-slot.md +++ /dev/null @@ -1,17 +0,0 @@ -# [S] An Option of numbers in the result slot - -## Goal - -A translated function that returns an `Option` of numbers alone, as `Decoder::varint_opt`'s -`Result, E>`, makes no object per call, with every differential test passing -and no workload on `just ts bench` meaningfully slower in any engine. - -## Plan - -Left by [#385](https://github.com/moq-dev/rust2/pull/385), which returns a `Result` of -numbers alone through #160's slot: the slot can't carry an `Option`, so `varint_opt` still -makes an `rt.Ok` per call. moq-net calls it 8 times against 249 for `varint`, so this is -low value; take it when a profile or a new caller shows it matters, or with other work on -the slot. - -Public API: the translator's output. diff --git a/ts/MIGRATE.md b/ts/MIGRATE.md index 23a08a159..eb5be7edb 100644 --- a/ts/MIGRATE.md +++ b/ts/MIGRATE.md @@ -46,8 +46,9 @@ whose object its body never makes as its `data`, `start`, and `length`: `read(buf, form)` is `read(buf.data, buf.start, buf.length, form)`. A caller takes which from the output. One that returns a `Result` of byte slices - and scalars, or of numbers alone, returns its error or `undefined`, and leaves the `Ok`'s parts in a - slot `lib.ts` exports, named for the payload type, such as `out$ref_slice_u8`. If the + and scalars, or of numbers alone, an `Option` of one counting as a number, such as + `Result, E>`, returns its error or `undefined`, and leaves the `Ok`'s parts in a + slot `lib.ts` exports, named for the payload type, such as `out$ref_slice_u8` or `out$Option_u64`. If the `Ok`'s first part is a number, it returns that, or `undefined` for an error, and leaves the rest, or the error as `err`, in the slot, such as `out$tuple2_u64_ref_slice_u8`. A root still takes an `rt.ByteSlice` and returns the `Result`. diff --git a/ts/README.md b/ts/README.md index 943d7b966..41b9e72eb 100644 --- a/ts/README.md +++ b/ts/README.md @@ -793,7 +793,8 @@ they need. the geometric mean, and frame decode at 0.61, 0.56, 0.70, and 0.61, with the A/A route within 0.04 and encode no slower. A tuple or `Result` of numbers alone is held so only where it meets a slot below, and its object, such as an `rt.Ok`, counts as made at each - read that makes it. A return is still a `ByteSlice` but for the `Result`s below. + read that makes it. An `Option` of a number is one part, that or `undefined`, as its JS + value is. A return is still a `ByteSlice` but for the `Result`s below. - A function only translated code calls takes each `&[u8]` or `&[u8; N]` parameter as three, its `data`, `start`, and `length`, which its body holds as that local's parts: not a root, which the outside calls, a closure, or an `async fn`. One whose object the @@ -809,8 +810,8 @@ they need. 1.01-1.05 over two runs, with the A/A route within 0.05, Firefox and Bun within their noise, and Node's varint decode at 1.11-1.15. - A function of the crate's that only translated code calls and that returns a `Result` - of byte slices and scalars, or of numbers alone, such as `Result<(u64, &[u8]), E>` or - `Result`, whose error is one JS + of byte slices and scalars, or of numbers alone, such as `Result<(u64, &[u8]), E>`, + `Result`, or `Result, E>`, whose error is one JS value that is never `undefined`, writes the `Ok`'s parts into a module-level slot and returns the error or `undefined`. The slot is shared by every function returning that payload type, in `lib.ts` and named for the type as the shims are, as `out$ref_slice_u8` or `out$u64`, so diff --git a/ts/fixtures/codec/expected/coding/decode.Decoder.ts b/ts/fixtures/codec/expected/coding/decode.Decoder.ts index 33bf00936..e697df418 100644 --- a/ts/fixtures/codec/expected/coding/decode.Decoder.ts +++ b/ts/fixtures/codec/expected/coding/decode.Decoder.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "../rt.ts"; -import { out$bool, out$ref_slice_u8, out$tuple2_u64_ref_slice_u8, out$u16, out$u64, out$u8 } from "../lib.ts"; +import { out$Option_u64, out$bool, out$ref_slice_u8, out$tuple2_u64_ref_slice_u8, out$u16, out$u64, out$u8 } from "../lib.ts"; import * as DecodeError from "../coding/decode.DecodeError.ts"; import { read } from "../coding/varint.ts"; import type * as Form from "../coding/varint.Form.ts"; @@ -190,13 +190,14 @@ export function varint(self: Decoder): rt.u64 | undefined { } /** Read an optional varint: 0 is `None`, and `n + 1` is `Some(n)`. */ -export function varintOpt(self: Decoder): rt.Result { +export function varintOpt(self: Decoder): DecodeError.DecodeError | undefined { const part: rt.u64 | undefined = varint(self); const err: DecodeError.DecodeError | undefined = part === undefined ? out$u64.err as DecodeError.DecodeError : undefined; if (err === undefined) { - return rt.Ok(rt.u64CheckedSub(part as rt.u64, 1)); + out$Option_u64.p = rt.u64CheckedSub(part as rt.u64, 1); + return undefined; } - return rt.Err(err as DecodeError.DecodeError); + return err as DecodeError.DecodeError; } /** Read a varint length, then that many raw bytes. */ diff --git a/ts/fixtures/codec/expected/harness.ts b/ts/fixtures/codec/expected/harness.ts index 69bdb3d9f..71c827d4f 100644 --- a/ts/fixtures/codec/expected/harness.ts +++ b/ts/fixtures/codec/expected/harness.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { out$bool, out$ref_slice_u8, out$u16, out$u64, out$u8, out$usize } from "./lib.ts"; +import { out$Option_u64, out$bool, out$ref_slice_u8, out$u16, out$u64, out$u8, out$usize } from "./lib.ts"; import * as DecodeError from "./coding/decode.DecodeError.ts"; import * as Decoder from "./coding/decode.Decoder.ts"; import * as EncodeError from "./coding/encode.EncodeError.ts"; @@ -224,41 +224,42 @@ export function decodeScript(dataData: Uint8Array, dataStart: number, dataLen: n export function decodeStep(r: Decoder.Decoder, step: rt.u8, out: rt.ByteVec): rt.Result { const _5: rt.u8 = rt.u8Rem(step, 10); if (_5 === 0) { - const part11: rt.u8 | undefined = Decoder.u8(r); - const err7: DecodeError.DecodeError | undefined = part11 === undefined ? out$u8.err as DecodeError.DecodeError : undefined; + const part12: rt.u8 | undefined = Decoder.u8(r); + const err8: DecodeError.DecodeError | undefined = part12 === undefined ? out$u8.err as DecodeError.DecodeError : undefined; + if (err8 === undefined) { + rt.byteVecPush(out, part12 as rt.u8); + } else { + return rt.Err(err8 as DecodeError.DecodeError); + } + } else if (_5 === 1) { + const part11: rt.u16 | undefined = Decoder.u16(r); + const err7: DecodeError.DecodeError | undefined = part11 === undefined ? out$u16.err as DecodeError.DecodeError : undefined; if (err7 === undefined) { - rt.byteVecPush(out, part11 as rt.u8); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u16ToBeBytes(part11 as rt.u16))); } else { return rt.Err(err7 as DecodeError.DecodeError); } - } else if (_5 === 1) { - const part10: rt.u16 | undefined = Decoder.u16(r); - const err6: DecodeError.DecodeError | undefined = part10 === undefined ? out$u16.err as DecodeError.DecodeError : undefined; + } else if (_5 === 2) { + const err6: DecodeError.DecodeError | undefined = Decoder.bool(r); + const part10: boolean = out$bool.p; if (err6 === undefined) { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u16ToBeBytes(part10 as rt.u16))); + rt.byteVecPush(out, part10 ? 1 : 0); } else { return rt.Err(err6 as DecodeError.DecodeError); } - } else if (_5 === 2) { - const err5: DecodeError.DecodeError | undefined = Decoder.bool(r); - const part9: boolean = out$bool.p; + } else if (_5 === 3) { + const part9: rt.u64 | undefined = Decoder.varint(r); + const err5: DecodeError.DecodeError | undefined = part9 === undefined ? out$u64.err as DecodeError.DecodeError : undefined; if (err5 === undefined) { - rt.byteVecPush(out, part9 ? 1 : 0); + putU64(out, part9 as rt.u64); } else { return rt.Err(err5 as DecodeError.DecodeError); } - } else if (_5 === 3) { - const part8: rt.u64 | undefined = Decoder.varint(r); - const err4: DecodeError.DecodeError | undefined = part8 === undefined ? out$u64.err as DecodeError.DecodeError : undefined; - if (err4 === undefined) { - putU64(out, part8 as rt.u64); - } else { - return rt.Err(err4 as DecodeError.DecodeError); - } } else if (_5 === 4) { - const _31: rt.Result = Decoder.varintOpt(r); - if (_31.ok) { - const val5: rt.u64 | undefined = _31.value; + const err4: DecodeError.DecodeError | undefined = Decoder.varintOpt(r); + const part8: rt.u64 | undefined = out$Option_u64.p; + if (err4 === undefined) { + const val5: rt.u64 | undefined = part8; if (val5 === undefined) { rt.byteVecPush(out, 0); } else { @@ -267,7 +268,7 @@ export function decodeStep(r: Decoder.Decoder, step: rt.u8, out: rt.ByteVec): rt putU64(out, v); } } else { - return _31; + return rt.Err(err4 as DecodeError.DecodeError); } } else if (_5 === 5) { const err3: DecodeError.DecodeError | undefined = Decoder.bytes(r); diff --git a/ts/fixtures/codec/expected/lib.ts b/ts/fixtures/codec/expected/lib.ts index 689bc6c9e..5edfc263a 100644 --- a/ts/fixtures/codec/expected/lib.ts +++ b/ts/fixtures/codec/expected/lib.ts @@ -7,6 +7,8 @@ export function copy$bench_Timestamp(v: Timestamp.Timestamp): Timestamp.Timestam return { value: v.value, scale: v.scale }; } +export const out$Option_u64: { p: rt.u64 | undefined } = { p: undefined as never }; + export const out$bool: { p: boolean } = { p: undefined as never }; export const out$ref_slice_u8: { pData: Uint8Array; pStart: number; pLen: number } = { pData: undefined as never, pStart: undefined as never, pLen: undefined as never }; diff --git a/ts/fixtures/lang/cells/expected/cells.ts b/ts/fixtures/lang/cells/expected/cells.ts index f052241be..f6bd1a136 100644 --- a/ts/fixtures/lang/cells/expected/cells.ts +++ b/ts/fixtures/lang/cells/expected/cells.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { out$u64, out$u8 } from "./lib.ts"; +import { out$Option_u8, out$u64, out$u8 } from "./lib.ts"; import { thenSome$0d5am30 } from "./core/bool.ts"; import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr } from "./core/option.ts"; import { err$1nbmqmx, map$1ma2nl9 as result_map$1ma2nl9, map$0l5uclx as result_map$0l5uclx, map$14n87rz as result_map$14n87rz, map$0h1vl84 as result_map$0h1vl84, map$03hz3l7 as result_map$03hz3l7, map$0yqi183 as result_map$0yqi183 } from "./core/result.ts"; @@ -153,20 +153,24 @@ export function cellOption(dataData: Uint8Array, dataStart: number, dataLen: num const _166: rt.u8 = rt.u8Rem(v2, 4); if (_166 === 0 || _166 === 1) { result = rt.onceGetOrTryInit(cell, (): rt.Result => { - return made$Option_u8(make, v2); + const err4 = made$Option_u8(make, v2); + return err4 !== undefined ? rt.Err(err4 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } else if (_166 === 2) { const _174: rt.OnceCell = cell; result = rt.onceGetOrTryInit(cell, (): rt.Result => { rt.onceSet(_174, make(v2 ^ 1), rt.None); - return made$Option_u8(make, v2); + const err3 = made$Option_u8(make, v2); + return err3 !== undefined ? rt.Err(err3 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } else { const _179: rt.OnceCell = cell; result = rt.onceGetOrTryInit(cell, (): rt.Result => { - return made$Option_u8(make, option_unwrapOr$0r2bmpr(err$1nbmqmx(result_map$1ma2nl9(rt.onceGetOrTryInit(_179, (): rt.Result => { - return made$Option_u8(make, v2 ^ 1); + const err2 = made$Option_u8(make, option_unwrapOr$0r2bmpr(err$1nbmqmx(result_map$1ma2nl9(rt.onceGetOrTryInit(_179, (): rt.Result => { + const err = made$Option_u8(make, v2 ^ 1); + return err !== undefined ? rt.Err(err as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None), (_22: rt.u8 | undefined): undefined => {})), v2)); + return err2 !== undefined ? rt.Err(err2 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } if (result.ok) { @@ -1289,20 +1293,24 @@ export function lockOption(dataData: Uint8Array, dataStart: number, dataLen: num const _166: rt.u8 = rt.u8Rem(v2, 4); if (_166 === 0 || _166 === 1) { result = rt.onceGetOrTryInit(cell, (): rt.Result => { - return made$Option_u8(make, v2); + const err4 = made$Option_u8(make, v2); + return err4 !== undefined ? rt.Err(err4 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } else if (_166 === 2) { const _174: rt.OnceLock = cell; result = rt.onceGetOrTryInit(cell, (): rt.Result => { rt.onceSet(_174, make(v2 ^ 1), rt.None); - return made$Option_u8(make, v2); + const err3 = made$Option_u8(make, v2); + return err3 !== undefined ? rt.Err(err3 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } else { const _179: rt.OnceLock = cell; result = rt.onceGetOrTryInit(cell, (): rt.Result => { - return made$Option_u8(make, option_unwrapOr$0r2bmpr(err$1nbmqmx(result_map$1ma2nl9(rt.onceGetOrTryInit(_179, (): rt.Result => { - return made$Option_u8(make, v2 ^ 1); + const err2 = made$Option_u8(make, option_unwrapOr$0r2bmpr(err$1nbmqmx(result_map$1ma2nl9(rt.onceGetOrTryInit(_179, (): rt.Result => { + const err = made$Option_u8(make, v2 ^ 1); + return err !== undefined ? rt.Err(err as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None), (_22: rt.u8 | undefined): undefined => {})), v2)); + return err2 !== undefined ? rt.Err(err2 as rt.u8) : rt.Ok(out$Option_u8.p); }, undefined, rt.None); } if (result.ok) { @@ -2309,11 +2317,12 @@ export function twin$ref_sync_OnceLock_unit(t: rt.OnceLock): } /** What `make` makes of `v`, or `v` back as an error for every third byte. */ -export function made$Option_u8(make: (a0: rt.u8) => rt.u8 | undefined, v: rt.u8): rt.Result { +export function made$Option_u8(make: (a0: rt.u8) => rt.u8 | undefined, v: rt.u8): rt.u8 | undefined { if (rt.u8Rem(v, 3) === 0) { - return rt.Err(v); + return v; } - return rt.Ok(make(v)); + out$Option_u8.p = make(v); + return undefined; } /** What `make` makes of `v`, or `v` back as an error for every third byte. */ diff --git a/ts/fixtures/lang/cells/expected/lib.ts b/ts/fixtures/lang/cells/expected/lib.ts index 6154dabe5..7b8be34ae 100644 --- a/ts/fixtures/lang/cells/expected/lib.ts +++ b/ts/fixtures/lang/cells/expected/lib.ts @@ -3,6 +3,8 @@ import * as rt from "./rt.ts"; import { cells } from "./cells.ts"; +export const out$Option_u8: { p: rt.u8 | undefined } = { p: undefined as never }; + export const out$u64: { err: unknown } = { err: undefined }; export const out$u8: { err: unknown } = { err: undefined }; diff --git a/ts/fixtures/lang/returns/expected/core/bool.ts b/ts/fixtures/lang/returns/expected/core/bool.ts index d3dfeb463..1a9e3a0ff 100644 --- a/ts/fixtures/lang/returns/expected/core/bool.ts +++ b/ts/fixtures/lang/returns/expected/core/bool.ts @@ -2,6 +2,13 @@ import * as rt from "../rt.ts"; +export function thenSome$0b23a33(self: boolean, t: rt.u16): rt.u16 | undefined { + if (!self) { + return undefined; + } + return t; +} + export function then$0w2ymju(self: boolean, f: () => rt.ByteSlice): rt.ByteSlice | undefined { if (!self) { return undefined; diff --git a/ts/fixtures/lang/returns/expected/core/option.ts b/ts/fixtures/lang/returns/expected/core/option.ts index 01fe14751..639f43d91 100644 --- a/ts/fixtures/lang/returns/expected/core/option.ts +++ b/ts/fixtures/lang/returns/expected/core/option.ts @@ -3,6 +3,20 @@ import * as rt from "../rt.ts"; import type * as returns_Error from "../returns.Error.ts"; +export function unwrapOr$06r8g1t(self: rt.i128 | undefined, default_: rt.i128): rt.i128 { + if (self === undefined) { + return default_; + } + return self; +} + +export function unwrapOr$1j4op5y(self: rt.u16 | undefined, default_: rt.u16): rt.u16 { + if (self === undefined) { + return default_; + } + return self; +} + export function unwrapOr$0r2bmpr(self: rt.u8 | undefined, default_: rt.u8): rt.u8 { if (self === undefined) { return default_; @@ -24,6 +38,20 @@ export function map$1yogfym(self: [rt.u8, rt.ByteSlice] | undefined, f: (a0: [rt return f(self); } +export function map$1cf8u0y(self: rt.u64 | undefined, f: (a0: rt.u64) => rt.u64): rt.u64 | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + +export function mapOr$1surhce(self: rt.u8 | undefined, default_: rt.u8, f: (a0: rt.u8) => rt.u8): rt.u8 { + if (self === undefined) { + return default_; + } + return f(self); +} + export function okOr$1b6nkwr(self: rt.char | undefined, err: rt.u8): rt.Result { if (self === undefined) { return rt.Err(err); @@ -31,14 +59,14 @@ export function okOr$1b6nkwr(self: rt.char | undefined, err: rt.u8): rt.Result { +export function okOr$12p77jh(self: rt.ByteSlice | undefined, err: returns_Error.Error): rt.Result { if (self === undefined) { return rt.Err(err); } return rt.Ok(self); } -export function okOr$12p77jh(self: rt.ByteSlice | undefined, err: returns_Error.Error): rt.Result { +export function okOr$0gtm15y(self: rt.ByteSlice | undefined, err: rt.u8 | undefined): rt.Result { if (self === undefined) { return rt.Err(err); } diff --git a/ts/fixtures/lang/returns/expected/core/result.ts b/ts/fixtures/lang/returns/expected/core/result.ts index 42c87b508..4544fbf0b 100644 --- a/ts/fixtures/lang/returns/expected/core/result.ts +++ b/ts/fixtures/lang/returns/expected/core/result.ts @@ -1,7 +1,17 @@ // Generated by rust2-ts. Do not edit. import * as rt from "../rt.ts"; -import type * as returns_Error from "../returns.Error.ts"; +import * as returns_Error from "../returns.Error.ts"; + +export function eq$11dy112(self: rt.Result, other: rt.Result): boolean { + if (!((self.ok ? 0 : 1) === (other.ok ? 0 : 1))) { + return false; + } + if (self.ok) { + return rt.eqOption(rt.u64Eq)(self.value, (other as { ok: true; value: rt.u64 | undefined }).value); + } + return returns_Error.eq$cmp_PartialEq(self.error, (other as { ok: false; error: returns_Error.Error }).error); +} export function map$1rffkv6(self: rt.Result, _: (a0: rt.u32) => rt.u64): rt.Result { if (self.ok) { diff --git a/ts/fixtures/lang/returns/expected/lib.ts b/ts/fixtures/lang/returns/expected/lib.ts index b14a48df1..6eb5aa5b7 100644 --- a/ts/fixtures/lang/returns/expected/lib.ts +++ b/ts/fixtures/lang/returns/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { returns } from "./returns.ts"; +import { returns, options } from "./returns.ts"; import * as At from "./returns.At.ts"; import type * as returns_Error from "./returns.Error.ts"; @@ -19,10 +19,44 @@ export class of$returns_At { } } +export const out$Option_bool: { p: boolean | undefined } = { p: undefined as never }; + +export const out$Option_char: { p: rt.char | undefined } = { p: undefined as never }; + +export const out$Option_f32: { p: rt.f32 | undefined } = { p: undefined as never }; + +export const out$Option_f64: { p: rt.f64 | undefined } = { p: undefined as never }; + +export const out$Option_i128: { p: rt.i128 | undefined } = { p: undefined as never }; + +export const out$Option_i16: { p: rt.i16 | undefined } = { p: undefined as never }; + +export const out$Option_i32: { p: rt.i32 | undefined } = { p: undefined as never }; + +export const out$Option_i64: { p: rt.i64 | undefined } = { p: undefined as never }; + +export const out$Option_i8: { p: rt.i8 | undefined } = { p: undefined as never }; + +export const out$Option_u128: { p: rt.u128 | undefined } = { p: undefined as never }; + +export const out$Option_u16: { p: rt.u16 | undefined } = { p: undefined as never }; + +export const out$Option_u32: { p: rt.u32 | undefined } = { p: undefined as never }; + +export const out$Option_u64: { p: rt.u64 | undefined } = { p: undefined as never }; + +export const out$Option_u8: { p: rt.u8 | undefined } = { p: undefined as never }; + +export const out$Option_usize: { p: rt.usize | undefined } = { p: undefined as never }; + export const out$bool: { p: boolean } = { p: undefined as never }; export const out$ref_slice_u8: { pData: Uint8Array; pStart: number; pLen: number } = { pData: undefined as never, pStart: undefined as never, pLen: undefined as never }; +export const out$tuple2_Option_u16_ref_slice_u8: { p_0: rt.u16 | undefined; p_1Data: Uint8Array; p_1Start: number; p_1Len: number } = { p_0: undefined as never, p_1Data: undefined as never, p_1Start: undefined as never, p_1Len: undefined as never }; + +export const out$tuple2_Option_u8_u64: { p_0: rt.u8 | undefined; p_1: rt.u64 } = { p_0: undefined as never, p_1: undefined as never }; + export const out$tuple2_f64_char: { err: unknown; p_1: rt.char } = { err: undefined, p_1: undefined as never }; export const out$tuple2_i64_u64: { err: unknown; p_1: rt.u64 } = { err: undefined, p_1: undefined as never }; @@ -33,6 +67,8 @@ export const out$tuple2_u16_ref_slice_u8: { err: unknown; p_1Data: Uint8Array; p export const out$tuple2_u32_ref_slice_u8: { err: unknown; p_1Data: Uint8Array; p_1Start: number; p_1Len: number } = { err: undefined, p_1Data: undefined as never, p_1Start: undefined as never, p_1Len: undefined as never }; +export const out$tuple2_u64_Option_i128: { err: unknown; p_1: rt.i128 | undefined } = { err: undefined, p_1: undefined as never }; + export const out$tuple2_u64_ref_slice_u8: { err: unknown; p_1Data: Uint8Array; p_1Start: number; p_1Len: number } = { err: undefined, p_1Data: undefined as never, p_1Start: undefined as never, p_1Len: undefined as never }; export const out$tuple2_u8_ref_slice_u8: { err: unknown; p_1Data: Uint8Array; p_1Start: number; p_1Len: number } = { err: undefined, p_1Data: undefined as never, p_1Start: undefined as never, p_1Len: undefined as never }; @@ -52,7 +88,7 @@ export const out$u8: { err: unknown } = { err: undefined }; * their count, so a case added at the end moves no other. */ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined][] { - return [["returns::returns", returns as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]]; + return [["returns::returns", returns as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["returns::options", options as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]]; } //# sourceMappingURL=lib.ts.map diff --git a/ts/fixtures/lang/returns/expected/returns.Error.ts b/ts/fixtures/lang/returns/expected/returns.Error.ts index 68fd7edad..2143f2c97 100644 --- a/ts/fixtures/lang/returns/expected/returns.Error.ts +++ b/ts/fixtures/lang/returns/expected/returns.Error.ts @@ -2,6 +2,18 @@ import * as rt from "./rt.ts"; +export function eq$cmp_PartialEq(self: Error, other: Error): boolean { + if (!({ Short: 0, Bad: 1 }[self.tag] === { Short: 0, Bad: 1 }[other.tag])) { + return false; + } + if (self.tag === "Short") { + if (other.tag === "Short") { + return self._0 === other._0; + } + } + return true; +} + export type Error = { tag: "Short"; _0: rt.usize } | { tag: "Bad" }; export function code(self: Error): rt.u8 { diff --git a/ts/fixtures/lang/returns/expected/returns.Opted.ts b/ts/fixtures/lang/returns/expected/returns.Opted.ts new file mode 100644 index 000000000..c88a874c1 --- /dev/null +++ b/ts/fixtures/lang/returns/expected/returns.Opted.ts @@ -0,0 +1,26 @@ +// Generated by rust2-ts. Do not edit. + +import { optU64 } from "./returns.ts"; + +/** A guard whose drop writes the slot after its function's call did. */ +export class Opted { + _0Data: Uint8Array; + _0Start: number; + _0Len: number; + + constructor(_0Data: Uint8Array, _0Start: number, _0Len: number) { + this._0Data = _0Data; + this._0Start = _0Start; + this._0Len = _0Len; + } + + [Symbol.dispose](): void { + drop$ops_Drop(this); + } +} + +export function drop$ops_Drop(self: Opted): void { + optU64(self._0Data, self._0Start, self._0Len); +} + +//# sourceMappingURL=returns.Opted.ts.map diff --git a/ts/fixtures/lang/returns/expected/returns.ts b/ts/fixtures/lang/returns/expected/returns.ts index 94aef3b09..752b32582 100644 --- a/ts/fixtures/lang/returns/expected/returns.ts +++ b/ts/fixtures/lang/returns/expected/returns.ts @@ -1,14 +1,15 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { of$returns_At, out$bool, out$ref_slice_u8, out$tuple2_f64_char, out$tuple2_i64_u64, out$tuple2_ref_slice_u8_ref_slice_u8, out$tuple2_u16_ref_slice_u8, out$tuple2_u32_ref_slice_u8, out$tuple2_u64_ref_slice_u8, out$tuple2_u8_ref_slice_u8, out$tuple3_ref_slice_u8_ref_array2_u8_ref_slice_u8, out$tuple3_u64_u64_ref_slice_u8, out$u32, out$u64, out$u8 } from "./lib.ts"; -import { then$0w2ymju } from "./core/bool.ts"; -import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$0c2keyb as option_map$0c2keyb, map$1yogfym as option_map$1yogfym, okOr$1b6nkwr as option_okOr$1b6nkwr, okOr$0gtm15y as option_okOr$0gtm15y, okOr$12p77jh as option_okOr$12p77jh, okOr$0tezn0w as option_okOr$0tezn0w } from "./core/option.ts"; -import { map$1rffkv6 as result_map$1rffkv6, mapOr$0fbyajt as result_mapOr$0fbyajt, andThen$1yropo4 as result_andThen$1yropo4 } from "./core/result.ts"; +import { of$returns_At, out$Option_bool, out$Option_char, out$Option_f32, out$Option_f64, out$Option_i128, out$Option_i16, out$Option_i32, out$Option_i64, out$Option_i8, out$Option_u128, out$Option_u16, out$Option_u32, out$Option_u64, out$Option_u8, out$Option_usize, out$bool, out$ref_slice_u8, out$tuple2_Option_u16_ref_slice_u8, out$tuple2_Option_u8_u64, out$tuple2_f64_char, out$tuple2_i64_u64, out$tuple2_ref_slice_u8_ref_slice_u8, out$tuple2_u16_ref_slice_u8, out$tuple2_u32_ref_slice_u8, out$tuple2_u64_Option_i128, out$tuple2_u64_ref_slice_u8, out$tuple2_u8_ref_slice_u8, out$tuple3_ref_slice_u8_ref_array2_u8_ref_slice_u8, out$tuple3_u64_u64_ref_slice_u8, out$u32, out$u64, out$u8 } from "./lib.ts"; +import { thenSome$0b23a33, then$0w2ymju } from "./core/bool.ts"; +import { unwrapOr$06r8g1t as option_unwrapOr$06r8g1t, unwrapOr$1j4op5y as option_unwrapOr$1j4op5y, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$0c2keyb as option_map$0c2keyb, map$1yogfym as option_map$1yogfym, map$1cf8u0y as option_map$1cf8u0y, mapOr$1surhce as option_mapOr$1surhce, okOr$1b6nkwr as option_okOr$1b6nkwr, okOr$12p77jh as option_okOr$12p77jh, okOr$0gtm15y as option_okOr$0gtm15y, okOr$0tezn0w as option_okOr$0tezn0w } from "./core/option.ts"; +import { eq$11dy112 as result_eq$11dy112, map$1rffkv6 as result_map$1rffkv6, mapOr$0fbyajt as result_mapOr$0fbyajt, andThen$1yropo4 as result_andThen$1yropo4 } from "./core/result.ts"; import * as At from "./returns.At.ts"; import * as returns_Error from "./returns.Error.ts"; import * as Guard from "./returns.Guard.ts"; import * as Late from "./returns.Late.ts"; +import * as Opted from "./returns.Opted.ts"; import * as Wrapped from "./returns.Wrapped.ts"; /** A slice alone, with returns from several places. */ @@ -799,4 +800,802 @@ export function returns(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } } +export function optF32(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_f32.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_f32.p = -0.0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_f32.p = NaN; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 4), { tag: "Short", _0: 4 } as returns_Error.Error); + if (_17.ok) { + out$Option_f32.p = rt.f32FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optF64(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_f64.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_f64.p = 0.0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_f64.p = NaN; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 8), { tag: "Short", _0: 8 } as returns_Error.Error); + if (_17.ok) { + out$Option_f64.p = rt.f64FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optI128(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_i128.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_i128.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_i128.p = { hi: -0x80000000, w2: 0, w1: 0, lo: 0 } as rt.i128; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 16), { tag: "Short", _0: 16 } as returns_Error.Error); + if (_17.ok) { + out$Option_i128.p = rt.i128FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optI16(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_i16.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_i16.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_i16.p = -32768; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 2), { tag: "Short", _0: 2 } as returns_Error.Error); + if (_17.ok) { + out$Option_i16.p = (((_17.value.data[_17.value.start] << 8) | _17.value.data[_17.value.start + 1]) << 16) >> 16; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optI32(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_i32.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_i32.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_i32.p = -2147483648; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 4), { tag: "Short", _0: 4 } as returns_Error.Error); + if (_17.ok) { + out$Option_i32.p = (_17.value.data[_17.value.start] << 24) | (_17.value.data[_17.value.start + 1] << 16) | (_17.value.data[_17.value.start + 2] << 8) | _17.value.data[_17.value.start + 3]; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optI64(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_i64.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_i64.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_i64.p = { hi: -0x80000000, lo: 0 } as rt.i64; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 8), { tag: "Short", _0: 8 } as returns_Error.Error); + if (_17.ok) { + out$Option_i64.p = rt.i64FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optI8(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_i8.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_i8.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_i8.p = -128; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 1), { tag: "Short", _0: 1 } as returns_Error.Error); + if (_17.ok) { + out$Option_i8.p = (_17.value.data[_17.value.start] << 24) >> 24; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optU128(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_u128.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_u128.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_u128.p = { hi: 0xffffffff, w2: 0xffffffff, w1: 0xffffffff, lo: 0xffffffff } as rt.u128; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 16), { tag: "Short", _0: 16 } as returns_Error.Error); + if (_17.ok) { + out$Option_u128.p = rt.u128FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optU16(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_u16.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_u16.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_u16.p = 65535; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 2), { tag: "Short", _0: 2 } as returns_Error.Error); + if (_17.ok) { + out$Option_u16.p = (_17.value.data[_17.value.start] << 8) | _17.value.data[_17.value.start + 1]; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optU32(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_u32.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_u32.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_u32.p = 4294967295; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 4), { tag: "Short", _0: 4 } as returns_Error.Error); + if (_17.ok) { + out$Option_u32.p = ((_17.value.data[_17.value.start] << 24) | (_17.value.data[_17.value.start + 1] << 16) | (_17.value.data[_17.value.start + 2] << 8) | _17.value.data[_17.value.start + 3]) >>> 0; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optU64(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_u64.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_u64.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_u64.p = { hi: 0xffffffff, lo: 0xffffffff } as rt.u64; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 8), { tag: "Short", _0: 8 } as returns_Error.Error); + if (_17.ok) { + out$Option_u64.p = rt.u64FromBeBytes(rt.byteSliceToArray(_17.value)); + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optU8(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_u8.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_u8.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_u8.p = 255; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 1), { tag: "Short", _0: 1 } as returns_Error.Error); + if (_17.ok) { + out$Option_u8.p = _17.value.data[_17.value.start]; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optUsize(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const _11: rt.u8 = rt.u8Rem(dataData[dataStart], 5); + if (_11 === 0) { + out$Option_usize.p = undefined; + return undefined; + } + if (_11 === 1) { + out$Option_usize.p = 0; + return undefined; + } + if (_11 === 2) { + return { tag: "Bad" } as returns_Error.Error; + } + if (_11 === 3) { + out$Option_usize.p = 4294967295; + return undefined; + } + const _17: rt.Result = option_okOr$12p77jh(rt.byteSliceGetRange(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1), 0, 4), { tag: "Short", _0: 4 } as returns_Error.Error); + if (_17.ok) { + out$Option_usize.p = ((_17.value.data[_17.value.start] << 24) | (_17.value.data[_17.value.start + 1] << 16) | (_17.value.data[_17.value.start + 2] << 8) | _17.value.data[_17.value.start + 3]) >>> 0; + return undefined; + } + return (_17 as { ok: false; error: returns_Error.Error }).error; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optBool(data: rt.ByteSlice): returns_Error.Error | undefined { + const _2: rt.u8 | undefined = rt.byteSliceFirst(data); + if (_2 === undefined) { + return { tag: "Short", _0: 1 } as returns_Error.Error; + } + if (!(rt.u8Rem(_2, 3) === 0)) { + out$Option_bool.p = rt.u8Rem(_2, 3) === 1; + return undefined; + } + out$Option_bool.p = undefined; + return undefined; +} + +export function optChar(dataData: Uint8Array, dataStart: number, dataLen: number): rt.u8 | undefined { + const part: rt.u8 | undefined = byte(new rt.ByteSlice(dataData, dataStart, dataLen), 0); + const err: rt.u8 | undefined = part === undefined ? out$u8.err as rt.u8 : undefined; + if (err === undefined) { + out$Option_char.p = rt.charFromU32(rt.u32Add(rt.u32Mul((part as rt.u8) as rt.u32, 435), 55040)); + return undefined; + } + return err as rt.u8; +} + +/** `opt_u64`'s result returned as is, and taken apart by `?` and made again. */ +export function optAgain(data: rt.ByteSlice, first: boolean): returns_Error.Error | undefined { + if (!first) { + const _6: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); + const err: returns_Error.Error | undefined = optU64(_6.data, _6.start, _6.length); + const part2: rt.u64 | undefined = out$Option_u64.p; + if (err === undefined) { + out$Option_u64.p = option_map$1cf8u0y(part2, (v: rt.u64): rt.u64 => { + return rt.u64Shr(v, 1); + }); + return undefined; + } + return err as returns_Error.Error; + } + return optU64(data.data, data.start, data.length); +} + +/** An `Option` beside a number, first and last, and beside a slice. */ +export function optFirst(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + const err: returns_Error.Error | undefined = optU8(dataData, dataStart, dataLen); + const part: rt.u8 | undefined = out$Option_u8.p; + if (err === undefined) { + out$tuple2_Option_u8_u64.p_0 = part; + out$tuple2_Option_u8_u64.p_1 = dataLen as rt.u64; + return undefined; + } + return err as returns_Error.Error; +} + +export function optLast(dataData: Uint8Array, dataStart: number, dataLen: number): rt.u64 | undefined { + const err: returns_Error.Error | undefined = optI128(dataData, dataStart, dataLen); + const part: rt.i128 | undefined = out$Option_i128.p; + const _3: rt.u64 = dataLen as rt.u64; + if (err === undefined) { + out$tuple2_u64_Option_i128.p_1 = part; + return _3; + } + out$tuple2_u64_Option_i128.err = err as returns_Error.Error; + return undefined; +} + +export function optRest(dataData: Uint8Array, dataStart: number, dataLen: number): returns_Error.Error | undefined { + if (dataLen !== 0) { + const b: rt.u8 = dataData[dataStart]; + out$tuple2_Option_u16_ref_slice_u8.p_0 = thenSome$0b23a33(rt.u8Rem(b, 2) === 0, (((b as rt.u16)) << 8) & 0xffff); + out$tuple2_Option_u16_ref_slice_u8.p_1Data = dataData; + out$tuple2_Option_u16_ref_slice_u8.p_1Start = dataStart + 1; + out$tuple2_Option_u16_ref_slice_u8.p_1Len = dataLen - 1; + return undefined; + } + return { tag: "Short", _0: 1 } as returns_Error.Error; +} + +export function optLate(dataData: Uint8Array, dataStart: number, dataLen: number, other: rt.ByteSlice): returns_Error.Error | undefined { + const err: returns_Error.Error | undefined = optU64(dataData, dataStart, dataLen); + const part: rt.u64 | undefined = out$Option_u64.p; + new Opted.Opted(other.data, other.start, other.length)[Symbol.dispose](); + if (err !== undefined) { + return err as returns_Error.Error; + } + out$Option_u64.p = part; + return undefined; +} + +/** A float's bits, or its sign alone if it is a NaN, whose payload is the engine's. */ +export function putFloat(out: rt.ByteVec, bits: rt.u64, nan: boolean, negative: boolean): void { + if (!nan) { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(bits))); + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(127, negative ? 1 : 0))); + } +} + +/** + * Results of `Option`s of numbers: held as parts, kept whole, and read after the slot was + * written again. + */ +export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { + let f!: (a0: rt.ByteSlice) => rt.Result; + const err: returns_Error.Error | undefined = optU8(data.data, data.start, data.length); + const part: rt.u8 | undefined = out$Option_u8.p; + if (err === undefined) { + if (part === undefined) { + rt.byteVecPush(out, 0); + } else { + const v: rt.u8 = part; + rt.byteVecPush(out, 1); + rt.byteVecPush(out, v); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err as returns_Error.Error)))); + } + const err2: returns_Error.Error | undefined = optU16(data.data, data.start, data.length); + const part2: rt.u16 | undefined = out$Option_u16.p; + if (err2 === undefined) { + if (part2 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v2: rt.u16 = part2; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u16ToBeBytes(v2))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err2 as returns_Error.Error)))); + } + const err3: returns_Error.Error | undefined = optU32(data.data, data.start, data.length); + const part3: rt.u32 | undefined = out$Option_u32.p; + if (err3 === undefined) { + if (part3 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v3: rt.u32 = part3; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(v3))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err3 as returns_Error.Error)))); + } + const err4: returns_Error.Error | undefined = optU64(data.data, data.start, data.length); + const part4: rt.u64 | undefined = out$Option_u64.p; + if (err4 === undefined) { + if (part4 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v4: rt.u64 = part4; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v4))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err4 as returns_Error.Error)))); + } + const err5: returns_Error.Error | undefined = optU128(data.data, data.start, data.length); + const part5: rt.u128 | undefined = out$Option_u128.p; + if (err5 === undefined) { + if (part5 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v5: rt.u128 = part5; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u128ToBeBytes(v5))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err5 as returns_Error.Error)))); + } + const err6: returns_Error.Error | undefined = optUsize(data.data, data.start, data.length); + const part6: rt.usize | undefined = out$Option_usize.p; + if (err6 === undefined) { + if (part6 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v6: rt.usize = part6; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v6 as rt.u64))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err6 as returns_Error.Error)))); + } + const err7: returns_Error.Error | undefined = optI8(data.data, data.start, data.length); + const part7: rt.i8 | undefined = out$Option_i8.p; + if (err7 === undefined) { + if (part7 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v7: rt.i8 = part7; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i8ToBeBytes(v7))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err7 as returns_Error.Error)))); + } + const err8: returns_Error.Error | undefined = optI16(data.data, data.start, data.length); + const part8: rt.i16 | undefined = out$Option_i16.p; + if (err8 === undefined) { + if (part8 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v8: rt.i16 = part8; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i16ToBeBytes(v8))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err8 as returns_Error.Error)))); + } + const err9: returns_Error.Error | undefined = optI32(data.data, data.start, data.length); + const part9: rt.i32 | undefined = out$Option_i32.p; + if (err9 === undefined) { + if (part9 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v9: rt.i32 = part9; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i32ToBeBytes(v9))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err9 as returns_Error.Error)))); + } + const err10: returns_Error.Error | undefined = optI64(data.data, data.start, data.length); + const part10: rt.i64 | undefined = out$Option_i64.p; + if (err10 === undefined) { + if (part10 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v10: rt.i64 = part10; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i64ToBeBytes(v10))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err10 as returns_Error.Error)))); + } + const err11: returns_Error.Error | undefined = optI128(data.data, data.start, data.length); + const part11: rt.i128 | undefined = out$Option_i128.p; + if (err11 === undefined) { + if (part11 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v11: rt.i128 = part11; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i128ToBeBytes(v11))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err11 as returns_Error.Error)))); + } + const err12: returns_Error.Error | undefined = optF32(data.data, data.start, data.length); + const part12: rt.f32 | undefined = out$Option_f32.p; + if (err12 === undefined) { + if (part12 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v12: rt.f32 = part12; + rt.byteVecPush(out, 1); + putFloat(out, rt.f32ToBits(v12) as rt.u64, Number.isNaN(v12), rt.f32IsSignNegative(v12)); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err12 as returns_Error.Error)))); + } + const err13: returns_Error.Error | undefined = optF64(data.data, data.start, data.length); + const part13: rt.f64 | undefined = out$Option_f64.p; + if (err13 === undefined) { + if (part13 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v13: rt.f64 = part13; + rt.byteVecPush(out, 1); + putFloat(out, rt.f64ToBits(v13), Number.isNaN(v13), rt.f64IsSignNegative(v13)); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err13 as returns_Error.Error)))); + } + const err14: returns_Error.Error | undefined = optBool(data); + const part14: boolean | undefined = out$Option_bool.p; + if (err14 === undefined) { + if (part14 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v14: boolean = part14; + rt.byteVecPush(out, 1); + rt.byteVecPush(out, v14 ? 1 : 0); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err14 as returns_Error.Error)))); + } + const err15: rt.u8 | undefined = optChar(data.data, data.start, data.length); + const part15: rt.char | undefined = out$Option_char.p; + if (err15 === undefined) { + if (part15 === undefined) { + rt.byteVecPush(out, 0); + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(part15 as rt.u32))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, err15 as rt.u8))); + } + const err16: returns_Error.Error | undefined = optAgain(data, rt.u8Rem(kind, 2) === 0); + const part16: rt.u64 | undefined = out$Option_u64.p; + if (err16 === undefined) { + if (part16 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v15: rt.u64 = part16; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v15))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err16 as returns_Error.Error)))); + } + const _244: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); + const err17: returns_Error.Error | undefined = optLate(data.data, data.start, data.length, _244); + const part18: rt.u64 | undefined = out$Option_u64.p; + if (err17 === undefined) { + if (part18 === undefined) { + rt.byteVecPush(out, 0); + } else { + const v16: rt.u64 = part18; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v16))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err17 as returns_Error.Error)))); + } + const err18: returns_Error.Error | undefined = optFirst(data.data, data.start, data.length); + const part19: rt.u8 | undefined = out$tuple2_Option_u8_u64.p_0; + const part20: rt.u64 = out$tuple2_Option_u8_u64.p_1; + if (err18 === undefined) { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(option_mapOr$1surhce(part19, 170, (n5: rt.u8): rt.u8 => { + return n5; + }), rt.u64Low(part20) & 0xff))); + } else { + rt.byteVecPush(out, returns_Error.code(err18 as returns_Error.Error)); + } + const part21: rt.u64 | undefined = optLast(data.data, data.start, data.length); + const err19: returns_Error.Error | undefined = part21 === undefined ? out$tuple2_u64_Option_i128.err as returns_Error.Error : undefined; + const part22: rt.i128 | undefined = out$tuple2_u64_Option_i128.p_1; + if (err19 === undefined) { + const len2: rt.u64 = part21 as rt.u64; + const n2: rt.i128 | undefined = part22; + rt.byteVecPush(out, rt.u64Low(len2) & 0xff); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i128ToBeBytes(option_unwrapOr$06r8g1t(n2, -1)))); + } else { + rt.byteVecPush(out, returns_Error.code(err19 as returns_Error.Error)); + } + const err20: returns_Error.Error | undefined = optRest(data.data, data.start, data.length); + const part23: rt.u16 | undefined = out$tuple2_Option_u16_ref_slice_u8.p_0; + const part26: number = out$tuple2_Option_u16_ref_slice_u8.p_1Len; + if (err20 === undefined) { + const n3: rt.u16 | undefined = part23; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(n3 !== undefined ? 1 : 0, (option_unwrapOr$1j4op5y(n3, 0) >> 8) & 0xff, part26 & 0xff))); + } else { + rt.byteVecPush(out, returns_Error.code(err20 as returns_Error.Error)); + } + const err21: returns_Error.Error | undefined = optU64(data.data, data.start, data.length); + const part27: rt.u64 | undefined = out$Option_u64.p; + const _312: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); + const err22: returns_Error.Error | undefined = optU64(_312.data, _312.start, _312.length); + const part29: rt.u64 | undefined = out$Option_u64.p; + b20: { + if (err21 === undefined) { + if (err22 === undefined) { + if (part27 !== undefined) { + if (part29 !== undefined) { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(rt.u64Xor(part27, part29)))); + break b20; + } + } + } + } + const x5: rt.Result = (err21 !== undefined ? rt.Err(err21 as returns_Error.Error) : rt.Ok(part27)) as rt.Result; + const y5: rt.Result = (err22 !== undefined ? rt.Err(err22 as returns_Error.Error) : rt.Ok(part29)) as rt.Result; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(x5.ok ? 1 : 0, y5.ok ? 1 : 0, result_eq$11dy112(x5, y5) ? 1 : 0))); + } + for (const r of rt.iterToArray(rt.iterMap(new rt.Range(0, 3), (i: rt.usize): rt.Result => { + const _3: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, i)); + const err23 = optU64(_3.data, _3.start, _3.length); + return err23 !== undefined ? rt.Err(err23 as returns_Error.Error) : rt.Ok(out$Option_u64.p); + }), undefined, 16, true)) { + if (r.ok) { + if (r.value === undefined) { + rt.byteVecPush(out, 0); + } else { + const v17: rt.u64 = r.value; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v17))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(r.error)))); + } + } + if (!(rt.u8Rem(kind, 2) === 0)) { + f = (d: rt.ByteSlice): rt.Result => { + const _33: rt.ByteSlice = rt.byteSliceRange(d, rt.min(d.length, 1)); + const err25 = optI64(_33.data, _33.start, _33.length); + return err25 !== undefined ? rt.Err(err25 as returns_Error.Error) : rt.Ok(out$Option_i64.p); + }; + } else { + f = (a0: rt.ByteSlice): rt.Result => { + const err24 = optI64(a0.data, a0.start, a0.length); + return err24 !== undefined ? rt.Err(err24 as returns_Error.Error) : rt.Ok(out$Option_i64.p); + }; + } + const _372: rt.Result = f(data); + if (_372.ok) { + if (_372.value === undefined) { + rt.byteVecPush(out, 0); + } else { + const v18: rt.i64 = _372.value; + rt.byteVecPush(out, 1); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i64ToBeBytes(v18))); + } + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(_372.error)))); + } + let rest2: rt.ByteSlice = data; + let sum: rt.u32 = 0; + while (true) { + const _390: rt.ByteSlice = rest2; + const err26: returns_Error.Error | undefined = optU32(_390.data, _390.start, _390.length); + const part32: rt.u32 | undefined = out$Option_u32.p; + if (err26 === undefined) { + if (part32 !== undefined) { + const n4: rt.u32 = part32; + sum = rt.u32WrappingAdd(rt.u32WrappingMul(sum, 31), n4); + rest2 = rt.byteSliceRange(rest2, rt.max(rt.min(rt.usizeRem(n4 as rt.usize, 5), rest2.length), 1)); + } else { + break; + } + } else { + break; + } + } + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(sum))); +} + //# sourceMappingURL=returns.ts.map diff --git a/ts/fixtures/lang/returns/src/lib.rs b/ts/fixtures/lang/returns/src/lib.rs index 6c6d828d3..58c7e6851 100644 --- a/ts/fixtures/lang/returns/src/lib.rs +++ b/ts/fixtures/lang/returns/src/lib.rs @@ -15,5 +15,5 @@ use harness::Case; /// Each case's name and what it runs. The first two bytes, big-endian, pick one modulo /// their count, so a case added at the end moves no other. fn cases() -> Vec<(&'static str, Case)> { - vec![("returns::returns", returns::returns)] + vec![("returns::returns", returns::returns), ("returns::options", returns::options)] } diff --git a/ts/fixtures/lang/returns/src/returns.rs b/ts/fixtures/lang/returns/src/returns.rs index 17bd25413..9f5a8eff7 100644 --- a/ts/fixtures/lang/returns/src/returns.rs +++ b/ts/fixtures/lang/returns/src/returns.rs @@ -1,5 +1,5 @@ -//! Results of byte slices, or of numbers alone, returned by the crate's own functions, -//! which hand back an `Ok`'s parts in a slot their caller reads at once. +//! Results of byte slices, or of numbers alone or in `Option`s, returned by the crate's own +//! functions, which hand back an `Ok`'s parts in a slot their caller reads at once. #[derive(Debug, Clone, Copy, PartialEq)] enum Error { @@ -425,3 +425,171 @@ pub fn returns(kind: u8, data: &[u8], out: &mut Vec) { } } } + +/// An `Option` of a number of each width, by the first byte: `None`, `Some` of zero, an +/// error, `Some` of the type's edge, or `Some` of the bytes after it. +macro_rules! option { + ($($name:ident: $t:ty = $zero:expr, $edge:expr;)*) => {$( + fn $name(data: &[u8]) -> Result, Error> { + let (&tag, rest) = data.split_first().ok_or(Error::Short(1))?; + match tag % 5 { + 0 => Ok(None), + 1 => Ok(Some($zero)), + 2 => Err(Error::Bad), + 3 => Ok(Some($edge)), + _ => { + let bytes = rest.first_chunk().ok_or(Error::Short(size_of::<$t>()))?; + Ok(Some(<$t>::from_be_bytes(*bytes))) + } + } + } + )*}; +} + +option! { + opt_u8: u8 = 0, u8::MAX; + opt_u16: u16 = 0, u16::MAX; + opt_u32: u32 = 0, u32::MAX; + opt_u64: u64 = 0, u64::MAX; + opt_u128: u128 = 0, u128::MAX; + opt_usize: usize = 0, usize::MAX; + opt_i8: i8 = 0, i8::MIN; + opt_i16: i16 = 0, i16::MIN; + opt_i32: i32 = 0, i32::MIN; + opt_i64: i64 = 0, i64::MIN; + opt_i128: i128 = 0, i128::MIN; + opt_f32: f32 = -0.0, f32::NAN; + opt_f64: f64 = 0.0, f64::NAN; +} + +fn opt_bool(data: &[u8]) -> Result, Error> { + match data.first() { + None => Err(Error::Short(1)), + Some(&b) if b % 3 == 0 => Ok(None), + Some(&b) => Ok(Some(b % 3 == 1)), + } +} + +fn opt_char(data: &[u8]) -> Result, u8> { + let b = byte(data, 0)?; + Ok(char::from_u32(u32::from(b) * 0x1b3 + 0xd700)) +} + +/// `opt_u64`'s result returned as is, and taken apart by `?` and made again. +fn opt_again(data: &[u8], first: bool) -> Result, Error> { + if first { + return opt_u64(data); + } + Ok(opt_u64(&data[data.len().min(1)..])?.map(|v| v >> 1)) +} + +/// An `Option` beside a number, first and last, and beside a slice. +fn opt_first(data: &[u8]) -> Result<(Option, u64), Error> { + let n = opt_u8(data)?; + Ok((n, data.len() as u64)) +} + +fn opt_last(data: &[u8]) -> Result<(u64, Option), Error> { + Ok((data.len() as u64, opt_i128(data)?)) +} + +fn opt_rest(data: &[u8]) -> Result<(Option, &[u8]), Error> { + let (&b, rest) = data.split_first().ok_or(Error::Short(1))?; + Ok(((b % 2 == 0).then_some(u16::from(b) << 8), rest)) +} + +/// A guard whose drop writes the slot after its function's call did. +struct Opted<'a>(&'a [u8]); + +impl Drop for Opted<'_> { + fn drop(&mut self) { + let _ = opt_u64(self.0); + } +} + +fn opt_late(data: &[u8], other: &[u8]) -> Result, Error> { + let _late = Opted(other); + opt_u64(data) +} + +/// A float's bits, or its sign alone if it is a NaN, whose payload is the engine's. +fn put_float(out: &mut Vec, bits: u64, nan: bool, negative: bool) { + match nan { + true => out.extend_from_slice(&[0x7f, negative as u8]), + false => out.extend_from_slice(&bits.to_be_bytes()), + } +} + +/// Results of `Option`s of numbers: held as parts, kept whole, and read after the slot was +/// written again. +pub fn options(kind: u8, data: &[u8], out: &mut Vec) { + macro_rules! push { + ($r:expr, |$v:ident| $put:expr) => { + match $r { + Ok(Some($v)) => { + out.push(1); + $put; + } + Ok(None) => out.push(0), + Err(e) => out.extend_from_slice(&[0xee, e.code()]), + } + }; + } + push!(opt_u8(data), |v| out.push(v)); + push!(opt_u16(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_u32(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_u64(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_u128(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_usize(data), |v| out.extend_from_slice(&(v as u64).to_be_bytes())); + push!(opt_i8(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_i16(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_i32(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_i64(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_i128(data), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_f32(data), |v| put_float(out, v.to_bits().into(), v.is_nan(), v.is_sign_negative())); + push!(opt_f64(data), |v| put_float(out, v.to_bits(), v.is_nan(), v.is_sign_negative())); + push!(opt_bool(data), |v| out.push(v as u8)); + match opt_char(data) { + Ok(Some(c)) => out.extend_from_slice(&(c as u32).to_be_bytes()), + Ok(None) => out.push(0), + Err(b) => out.extend_from_slice(&[0xee, b]), + } + push!(opt_again(data, kind % 2 == 0), |v| out.extend_from_slice(&v.to_be_bytes())); + push!(opt_late(data, &data[data.len().min(1)..]), |v| out.extend_from_slice(&v.to_be_bytes())); + match opt_first(data) { + Ok((n, len)) => out.extend_from_slice(&[n.map_or(0xaa, |n| n), len as u8]), + Err(e) => out.push(e.code()), + } + match opt_last(data) { + Ok((len, n)) => { + out.push(len as u8); + out.extend_from_slice(&n.unwrap_or(-1).to_be_bytes()); + } + Err(e) => out.push(e.code()), + } + match opt_rest(data) { + Ok((n, rest)) => out.extend_from_slice(&[n.is_some() as u8, (n.unwrap_or(0) >> 8) as u8, rest.len() as u8]), + Err(e) => out.push(e.code()), + } + // Both held while the slot is written again between them. + let (x, y) = (opt_u64(data), opt_u64(&data[data.len().min(1)..])); + match (x, y) { + (Ok(Some(x)), Ok(Some(y))) => out.extend_from_slice(&(x ^ y).to_be_bytes()), + (x, y) => out.extend_from_slice(&[x.is_ok() as u8, y.is_ok() as u8, (x == y) as u8]), + } + // Kept whole: in a collection, and through a function as a value. + let kept: Vec, Error>> = (0..3).map(|i| opt_u64(&data[data.len().min(i)..])).collect(); + for r in kept { + push!(r, |v| out.extend_from_slice(&v.to_be_bytes())); + } + let f: fn(&[u8]) -> Result, Error> = if kind % 2 == 0 { opt_i64 } else { |d| opt_i64(&d[d.len().min(1)..]) }; + push!(f(data), |v| out.extend_from_slice(&v.to_be_bytes())); + // A loop that calls again while the last result is still read. + let mut rest = data; + let mut sum = 0u32; + while let Ok(Some(n)) = opt_u32(rest) { + sum = sum.wrapping_mul(31).wrapping_add(n); + rest = &rest[(n as usize % 5).min(rest.len()).max(1)..]; + } + out.extend_from_slice(&sum.to_be_bytes()); +} diff --git a/ts/fixtures/lang/shapes/expected/lib.ts b/ts/fixtures/lang/shapes/expected/lib.ts index f25959480..790e4e8fe 100644 --- a/ts/fixtures/lang/shapes/expected/lib.ts +++ b/ts/fixtures/lang/shapes/expected/lib.ts @@ -141,6 +141,8 @@ export function drop$generics_Run_generics_Ticked(_1: Run.Run): v } } +export const out$Option_u8: { p: rt.u8 | undefined } = { p: undefined as never }; + export const out$u8: { err: unknown } = { err: undefined }; /** diff --git a/ts/fixtures/lang/shapes/expected/shapes.ts b/ts/fixtures/lang/shapes/expected/shapes.ts index 1f158fc37..4a2717d9b 100644 --- a/ts/fixtures/lang/shapes/expected/shapes.ts +++ b/ts/fixtures/lang/shapes/expected/shapes.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { copy$Option_shapes_Shape, copy$Option_tuple2_ref_sync_Mutex_u8_u8, copy$Result_tuple2_ref_sync_Mutex_u8_u8_u8, copy$array_ref_sync_Mutex_u8, copy$array_shapes_Point, copy$array_u8, copy$ops_ControlFlow_ref_sync_Arc_u8_array1_u8, copy$ops_ControlFlow_unit_array2_u8, copy$shapes_Gauge, copy$shapes_Held, copy$shapes_Lens, copy$shapes_Level, copy$shapes_Point, copy$shapes_Shape, copy$task_Poll_array1_u8, copy$tuple2_ref_sync_Mutex_u8_u8, copy$tuple2_shapes_Point_u8, copy$tuple2_u8_u8, out$u8 } from "./lib.ts"; +import { copy$Option_shapes_Shape, copy$Option_tuple2_ref_sync_Mutex_u8_u8, copy$Result_tuple2_ref_sync_Mutex_u8_u8_u8, copy$array_ref_sync_Mutex_u8, copy$array_shapes_Point, copy$array_u8, copy$ops_ControlFlow_ref_sync_Arc_u8_array1_u8, copy$ops_ControlFlow_unit_array2_u8, copy$shapes_Gauge, copy$shapes_Held, copy$shapes_Lens, copy$shapes_Level, copy$shapes_Point, copy$shapes_Shape, copy$task_Poll_array1_u8, copy$tuple2_ref_sync_Mutex_u8_u8, copy$tuple2_shapes_Point_u8, copy$tuple2_u8_u8, out$Option_u8, out$u8 } from "./lib.ts"; import { thenSome$0ztng9q, then$1i9qaz4, then$1btzga3 } from "./core/bool.ts"; import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$0600mq0 as option_map$0600mq0, map$18z3v8h as option_map$18z3v8h, mapOr$1a3irjf as option_mapOr$1a3irjf, mapOr$1t0muvr as option_mapOr$1t0muvr, mapOr$1ybqvup as option_mapOr$1ybqvup, mapOr$0rtdylj as option_mapOr$0rtdylj, mapOr$0aserv9 as option_mapOr$0aserv9, mapOr$1hjjwvv as option_mapOr$1hjjwvv, okOr$1vxk0yk as option_okOr$1vxk0yk } from "./core/option.ts"; import { map$1dbyvzi as result_map$1dbyvzi, mapErr$0dzqwjy as result_mapErr$0dzqwjy } from "./core/result.ts"; @@ -2370,8 +2370,9 @@ export function library(data: rt.ByteSlice, out: rt.ByteVec): void { } /** A result with nothing set yet. */ -export function unset(): rt.Result { - return rt.OK; +export function unset(): rt.u8 | undefined { + out$Option_u8.p = undefined; + return undefined; } /** Steps a total by `x`: breaks with what it can't take, or continues with the new total. */ @@ -2478,11 +2479,15 @@ export function flows(data: rt.ByteSlice, out: rt.ByteVec): void { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(flow3.value)); } } - let set: rt.Result = unset(); + const err = unset(); + let set: rt.Result = err !== undefined ? rt.Err(err as rt.u8) : rt.Ok(out$Option_u8.p); if (set.ok) { set = rt.Ok(a); } - const iter3: rt.Iter> = rt.iterSlice([set, unset()]); + const _108: rt.Result = set; + const err2: rt.u8 | undefined = unset(); + const part: rt.u8 | undefined = out$Option_u8.p; + const iter3: rt.Iter> = rt.iterSlice([_108, (err2 !== undefined ? rt.Err(err2 as rt.u8) : rt.Ok(part)) as rt.Result]); while (true) { const _111: rt.Result | rt.Done = iter3.next(); if (_111 === rt.DONE) { diff --git a/ts/fixtures/lang/splits/expected/core/option.ts b/ts/fixtures/lang/splits/expected/core/option.ts index 2a488c6a7..5ee85205f 100644 --- a/ts/fixtures/lang/splits/expected/core/option.ts +++ b/ts/fixtures/lang/splits/expected/core/option.ts @@ -16,11 +16,4 @@ export function mapOr$11e0ggl(self: [rt.ByteSlice, rt.ByteSlice] | undefined, de return f(self); } -export function okOr$0q0ogpn(self: [rt.ByteSlice, rt.ByteSlice] | undefined, err: rt.u8 | undefined): rt.Result<[rt.ByteSlice, rt.ByteSlice], rt.u8 | undefined> { - if (self === undefined) { - return rt.Err(err); - } - return rt.Ok(self); -} - //# sourceMappingURL=option.ts.map diff --git a/ts/fixtures/lang/splits/expected/splits.ts b/ts/fixtures/lang/splits/expected/splits.ts index 201300562..1b090fe00 100644 --- a/ts/fixtures/lang/splits/expected/splits.ts +++ b/ts/fixtures/lang/splits/expected/splits.ts @@ -2,7 +2,7 @@ import * as rt from "./rt.ts"; import { out$tuple2_ref_slice_u8_ref_slice_u8, out$tuple3_u8_u16_ref_slice_u8 } from "./lib.ts"; -import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, mapOr$11e0ggl as option_mapOr$11e0ggl, okOr$0q0ogpn as option_okOr$0q0ogpn } from "./core/option.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, mapOr$11e0ggl as option_mapOr$11e0ggl } from "./core/option.ts"; import type * as Short from "./splits.Short.ts"; /** The first byte, then a big-endian `u16`, by `?` on `ok_or`. */ @@ -51,11 +51,10 @@ export function take(dataData: Uint8Array, dataStart: number, dataLen: number, n /** The byte after the first, or an error that is itself an enum, held whole. */ export function second(dataData: Uint8Array, dataStart: number, dataLen: number, e: rt.u8 | undefined): rt.Result { - const _4: rt.Result<[rt.ByteSlice, rt.ByteSlice], rt.u8 | undefined> = option_okOr$0q0ogpn((1 <= dataLen ? [new rt.ByteSlice(dataData, dataStart, 1), new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1)] as [rt.ByteSlice, rt.ByteSlice] : undefined) as [rt.ByteSlice, rt.ByteSlice] | undefined, e); - if (_4.ok) { - return rt.Ok(option_unwrapOr$0r2bmpr(rt.byteSliceFirst(_4.value[1]), 0)); + if (1 <= dataLen) { + return rt.Ok(option_unwrapOr$0r2bmpr(rt.byteSliceFirst(new rt.ByteSlice(dataData, dataStart + 1, dataLen - 1)), 0)); } - return _4; + return rt.Err(e); } /** A use that needs the slice itself. */ diff --git a/ts/src/lower/mir/scalar.rs b/ts/src/lower/mir/scalar.rs index e6a482f92..013980bde 100644 --- a/ts/src/lower/mir/scalar.rs +++ b/ts/src/lower/mir/scalar.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::{ StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{Span, Spanned}; use super::Fx; @@ -32,7 +32,8 @@ use crate::ts::{Expr, Key, Stmt, StmtKind, Type}; pub enum Shape<'tcx> { /// No JS value: `()`. Unit, - /// One JS value, held whole: a number, a reference to one, or an error. + /// One JS value, held whole: a number, a reference to one, an `Option` of one, or an + /// error. Leaf(Ty<'tcx>), /// `&[u8]` or `&[u8; N]`: its bytes, start, and length. Slice, @@ -136,11 +137,11 @@ impl<'tcx> Shape<'tcx> { /// The parts that are JS objects as the local's object holds them: each slice, by its /// bytes, and each value held whole that is no number. - fn objects(&self, at: usize, out: &mut Vec) { + fn objects(&self, tcx: TyCtxt<'tcx>, at: usize, out: &mut Vec) { match self { Shape::Unit => {} Shape::Leaf(ty) => { - if !number(*ty) { + if !number(tcx, *ty) { out.push(at); } } @@ -148,14 +149,14 @@ impl<'tcx> Shape<'tcx> { Shape::Tuple(_, fields) => { let mut at = at; for field in fields { - field.objects(at, out); + field.objects(tcx, at, out); at += field.size(); } } Shape::Enum(_, variants) => { let mut at = at + usize::from(self.flagged()); for field in variants.iter().flatten().flatten() { - field.objects(at, out); + field.objects(tcx, at, out); at += field.size(); } } @@ -184,11 +185,20 @@ fn flag_name(ty: Ty<'_>) -> &'static str { } } -/// Whether a `ty`'s JS value is a number or flag, which making its object makes nothing of. -fn number(ty: Ty<'_>) -> bool { +/// Whether a `ty`'s JS value is a number or flag, which making its object makes nothing of, +/// or an `Option` of one, which is that or `undefined`. +fn number<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(adt, args) if Std::of(tcx, adt.did()) == Some(Std::Option) => plain_number(args.type_at(0)), + _ => plain_number(ty), + } +} + +/// Whether a `ty`'s JS value is a number or flag. +fn plain_number(ty: Ty<'_>) -> bool { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => true, - ty::Ref(_, inner, _) => number(*inner), + ty::Ref(_, inner, _) => plain_number(*inner), _ => false, } } @@ -320,8 +330,9 @@ impl<'tcx> crate::lower::Translator<'_, 'tcx> { /// Where `instance` hands back its `Ok`'s parts, if it does: a function of the crate's /// that only translated code calls directly, and that returns a `Result` of byte slices - /// and scalars, or of numbers alone, whose error is one value that is never - /// `undefined`, such as `Result<(u64, &[u8]), E>` or `Result<(i64, u64), E>`, writes + /// and scalars, or of numbers alone, an `Option` of one counting as one, whose error is + /// one value that is never `undefined`, such as `Result<(u64, &[u8]), E>`, + /// `Result<(i64, u64), E>`, or `Result, E>`, writes /// the `Ok`'s parts into a module-level slot its caller reads at once, and returns its /// error or `undefined`. One whose first part is a number returns that, or `undefined` /// for an error, and writes the error into the slot instead. @@ -361,7 +372,7 @@ impl<'tcx> crate::lower::Translator<'_, 'tcx> { let numbers = !parts.is_empty() && parts .iter() - .all(|(_, part)| matches!(part, Part::Value(t) if number(*t))); + .all(|(_, part)| matches!(part, Part::Value(t) if number(self.tcx, *t))); if !shape.has_slice() && !numbers { return None; } @@ -474,6 +485,14 @@ impl<'tcx> crate::lower::Translator<'_, 'tcx> { let fields = tys.iter().map(|t| self.shape(t)).collect::>>()?; Some(Shape::Tuple(ty, fields)) } + // That or `undefined`, one JS value, as the number is. + ty::Adt(adt, args) + if Std::of(tcx, adt.did()) == Some(Std::Option) + && plain_number(args.type_at(0)) + && matches!(self.shape(args.type_at(0)), Some(Shape::Leaf(_))) => + { + Some(Shape::Leaf(ty)) + } ty::Adt(adt, args) if matches!( Std::of(tcx, adt.did()), @@ -1297,7 +1316,7 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { let mut parts = Vec::new(); match made.boxed { true => parts.extend(made.parts.clone()), - false => shapes[&made.local].objects(0, &mut parts), + false => shapes[&made.local].objects(self.tcx, 0, &mut parts), } parts .into_iter() From 6172b7e6bdb51eec4961729d7bfa8d6fcd26c7e5 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:50:28 -0800 Subject: [PATCH 3/3] test(ts): write each Option case's number through one trait Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/returns/expected/returns.ts | 158 +++++++++++++------ ts/fixtures/lang/returns/src/lib.rs | 5 +- ts/fixtures/lang/returns/src/returns.rs | 115 ++++++++++---- 3 files changed, 195 insertions(+), 83 deletions(-) diff --git a/ts/fixtures/lang/returns/expected/returns.ts b/ts/fixtures/lang/returns/expected/returns.ts index 752b32582..cedbd653e 100644 --- a/ts/fixtures/lang/returns/expected/returns.ts +++ b/ts/fixtures/lang/returns/expected/returns.ts @@ -565,7 +565,7 @@ export function numbers(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void break; } } - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(sum))); + put$returns_Put_u64(sum, out); } export function returns(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { @@ -1251,12 +1251,71 @@ export function optLate(dataData: Uint8Array, dataStart: number, dataLen: number return undefined; } -/** A float's bits, or its sign alone if it is a NaN, whose payload is the engine's. */ -export function putFloat(out: rt.ByteVec, bits: rt.u64, nan: boolean, negative: boolean): void { - if (!nan) { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(bits))); +export function put$returns_Put_i128(self: rt.i128, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i128ToBeBytes(self))); +} + +export function put$returns_Put_i16(self: rt.i16, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i16ToBeBytes(self))); +} + +export function put$returns_Put_i32(self: rt.i32, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i32ToBeBytes(self))); +} + +export function put$returns_Put_i64(self: rt.i64, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i64ToBeBytes(self))); +} + +export function put$returns_Put_i8(self: rt.i8, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i8ToBeBytes(self))); +} + +export function put$returns_Put_u128(self: rt.u128, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u128ToBeBytes(self))); +} + +export function put$returns_Put_u16(self: rt.u16, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u16ToBeBytes(self))); +} + +export function put$returns_Put_u32(self: rt.u32, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(self))); +} + +export function put$returns_Put_u64(self: rt.u64, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(self))); +} + +export function put$returns_Put_u8(self: rt.u8, out: rt.ByteVec): void { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u8ToBeBytes(self))); +} + +export function put$returns_Put_usize(self: rt.usize, out: rt.ByteVec): void { + put$returns_Put_u64(self as rt.u64, out); +} + +export function put$returns_Put_bool(self: boolean, out: rt.ByteVec): void { + rt.byteVecPush(out, self ? 1 : 0); +} + +export function put$returns_Put_char(self: rt.char, out: rt.ByteVec): void { + put$returns_Put_u32(self as rt.u32, out); +} + +export function put$returns_Put_f32(self: rt.f32, out: rt.ByteVec): void { + if (!Number.isNaN(self)) { + put$returns_Put_u32(rt.f32ToBits(self), out); + } else { + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(127, rt.f32IsSignNegative(self) ? 1 : 0))); + } +} + +export function put$returns_Put_f64(self: rt.f64, out: rt.ByteVec): void { + if (!Number.isNaN(self)) { + put$returns_Put_u64(rt.f64ToBits(self), out); } else { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(127, negative ? 1 : 0))); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(127, rt.f64IsSignNegative(self) ? 1 : 0))); } } @@ -1274,7 +1333,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v: rt.u8 = part; rt.byteVecPush(out, 1); - rt.byteVecPush(out, v); + put$returns_Put_u8(v, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err as returns_Error.Error)))); @@ -1287,7 +1346,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v2: rt.u16 = part2; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u16ToBeBytes(v2))); + put$returns_Put_u16(v2, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err2 as returns_Error.Error)))); @@ -1300,7 +1359,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v3: rt.u32 = part3; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(v3))); + put$returns_Put_u32(v3, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err3 as returns_Error.Error)))); @@ -1313,7 +1372,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v4: rt.u64 = part4; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v4))); + put$returns_Put_u64(v4, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err4 as returns_Error.Error)))); @@ -1326,7 +1385,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v5: rt.u128 = part5; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u128ToBeBytes(v5))); + put$returns_Put_u128(v5, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err5 as returns_Error.Error)))); @@ -1339,7 +1398,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v6: rt.usize = part6; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v6 as rt.u64))); + put$returns_Put_usize(v6, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err6 as returns_Error.Error)))); @@ -1352,7 +1411,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v7: rt.i8 = part7; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i8ToBeBytes(v7))); + put$returns_Put_i8(v7, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err7 as returns_Error.Error)))); @@ -1365,7 +1424,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v8: rt.i16 = part8; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i16ToBeBytes(v8))); + put$returns_Put_i16(v8, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err8 as returns_Error.Error)))); @@ -1378,7 +1437,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v9: rt.i32 = part9; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i32ToBeBytes(v9))); + put$returns_Put_i32(v9, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err9 as returns_Error.Error)))); @@ -1391,7 +1450,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v10: rt.i64 = part10; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i64ToBeBytes(v10))); + put$returns_Put_i64(v10, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err10 as returns_Error.Error)))); @@ -1404,7 +1463,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v11: rt.i128 = part11; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i128ToBeBytes(v11))); + put$returns_Put_i128(v11, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err11 as returns_Error.Error)))); @@ -1417,7 +1476,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v12: rt.f32 = part12; rt.byteVecPush(out, 1); - putFloat(out, rt.f32ToBits(v12) as rt.u64, Number.isNaN(v12), rt.f32IsSignNegative(v12)); + put$returns_Put_f32(v12, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err12 as returns_Error.Error)))); @@ -1430,7 +1489,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v13: rt.f64 = part13; rt.byteVecPush(out, 1); - putFloat(out, rt.f64ToBits(v13), Number.isNaN(v13), rt.f64IsSignNegative(v13)); + put$returns_Put_f64(v13, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err13 as returns_Error.Error)))); @@ -1443,7 +1502,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v14: boolean = part14; rt.byteVecPush(out, 1); - rt.byteVecPush(out, v14 ? 1 : 0); + put$returns_Put_bool(v14, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err14 as returns_Error.Error)))); @@ -1454,26 +1513,26 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void if (part15 === undefined) { rt.byteVecPush(out, 0); } else { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(part15 as rt.u32))); + put$returns_Put_char(part15, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, err15 as rt.u8))); } + const tail: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); const err16: returns_Error.Error | undefined = optAgain(data, rt.u8Rem(kind, 2) === 0); - const part16: rt.u64 | undefined = out$Option_u64.p; + const part17: rt.u64 | undefined = out$Option_u64.p; if (err16 === undefined) { - if (part16 === undefined) { + if (part17 === undefined) { rt.byteVecPush(out, 0); } else { - const v15: rt.u64 = part16; + const v15: rt.u64 = part17; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v15))); + put$returns_Put_u64(v15, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err16 as returns_Error.Error)))); } - const _244: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); - const err17: returns_Error.Error | undefined = optLate(data.data, data.start, data.length, _244); + const err17: returns_Error.Error | undefined = optLate(data.data, data.start, data.length, tail); const part18: rt.u64 | undefined = out$Option_u64.p; if (err17 === undefined) { if (part18 === undefined) { @@ -1481,7 +1540,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v16: rt.u64 = part18; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v16))); + put$returns_Put_u64(v16, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(err17 as returns_Error.Error)))); @@ -1503,7 +1562,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void const len2: rt.u64 = part21 as rt.u64; const n2: rt.i128 | undefined = part22; rt.byteVecPush(out, rt.u64Low(len2) & 0xff); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i128ToBeBytes(option_unwrapOr$06r8g1t(n2, -1)))); + put$returns_Put_i128(option_unwrapOr$06r8g1t(n2, -1), out); } else { rt.byteVecPush(out, returns_Error.code(err19 as returns_Error.Error)); } @@ -1518,22 +1577,21 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } const err21: returns_Error.Error | undefined = optU64(data.data, data.start, data.length); const part27: rt.u64 | undefined = out$Option_u64.p; - const _312: rt.ByteSlice = rt.byteSliceRange(data, rt.min(data.length, 1)); - const err22: returns_Error.Error | undefined = optU64(_312.data, _312.start, _312.length); - const part29: rt.u64 | undefined = out$Option_u64.p; + const err22: returns_Error.Error | undefined = optU64(tail.data, tail.start, tail.length); + const part28: rt.u64 | undefined = out$Option_u64.p; b20: { if (err21 === undefined) { if (err22 === undefined) { if (part27 !== undefined) { - if (part29 !== undefined) { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(rt.u64Xor(part27, part29)))); + if (part28 !== undefined) { + put$returns_Put_u64(rt.u64Xor(part27, part28), out); break b20; } } } } const x5: rt.Result = (err21 !== undefined ? rt.Err(err21 as returns_Error.Error) : rt.Ok(part27)) as rt.Result; - const y5: rt.Result = (err22 !== undefined ? rt.Err(err22 as returns_Error.Error) : rt.Ok(part29)) as rt.Result; + const y5: rt.Result = (err22 !== undefined ? rt.Err(err22 as returns_Error.Error) : rt.Ok(part28)) as rt.Result; rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(x5.ok ? 1 : 0, y5.ok ? 1 : 0, result_eq$11dy112(x5, y5) ? 1 : 0))); } for (const r of rt.iterToArray(rt.iterMap(new rt.Range(0, 3), (i: rt.usize): rt.Result => { @@ -1547,7 +1605,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } else { const v17: rt.u64 = r.value; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(v17))); + put$returns_Put_u64(v17, out); } } else { rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(r.error)))); @@ -1555,8 +1613,8 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void } if (!(rt.u8Rem(kind, 2) === 0)) { f = (d: rt.ByteSlice): rt.Result => { - const _33: rt.ByteSlice = rt.byteSliceRange(d, rt.min(d.length, 1)); - const err25 = optI64(_33.data, _33.start, _33.length); + const _32: rt.ByteSlice = rt.byteSliceRange(d, rt.min(d.length, 1)); + const err25 = optI64(_32.data, _32.start, _32.length); return err25 !== undefined ? rt.Err(err25 as returns_Error.Error) : rt.Ok(out$Option_i64.p); }; } else { @@ -1565,27 +1623,27 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void return err24 !== undefined ? rt.Err(err24 as returns_Error.Error) : rt.Ok(out$Option_i64.p); }; } - const _372: rt.Result = f(data); - if (_372.ok) { - if (_372.value === undefined) { + const _310: rt.Result = f(data); + if (_310.ok) { + if (_310.value === undefined) { rt.byteVecPush(out, 0); } else { - const v18: rt.i64 = _372.value; + const v18: rt.i64 = _310.value; rt.byteVecPush(out, 1); - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.i64ToBeBytes(v18))); + put$returns_Put_i64(v18, out); } } else { - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(_372.error)))); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(238, returns_Error.code(_310.error)))); } let rest2: rt.ByteSlice = data; let sum: rt.u32 = 0; while (true) { - const _390: rt.ByteSlice = rest2; - const err26: returns_Error.Error | undefined = optU32(_390.data, _390.start, _390.length); - const part32: rt.u32 | undefined = out$Option_u32.p; + const _325: rt.ByteSlice = rest2; + const err26: returns_Error.Error | undefined = optU32(_325.data, _325.start, _325.length); + const part31: rt.u32 | undefined = out$Option_u32.p; if (err26 === undefined) { - if (part32 !== undefined) { - const n4: rt.u32 = part32; + if (part31 !== undefined) { + const n4: rt.u32 = part31; sum = rt.u32WrappingAdd(rt.u32WrappingMul(sum, 31), n4); rest2 = rt.byteSliceRange(rest2, rt.max(rt.min(rt.usizeRem(n4 as rt.usize, 5), rest2.length), 1)); } else { @@ -1595,7 +1653,7 @@ export function options(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void break; } } - rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u32ToBeBytes(sum))); + put$returns_Put_u32(sum, out); } //# sourceMappingURL=returns.ts.map diff --git a/ts/fixtures/lang/returns/src/lib.rs b/ts/fixtures/lang/returns/src/lib.rs index 58c7e6851..bc80c1600 100644 --- a/ts/fixtures/lang/returns/src/lib.rs +++ b/ts/fixtures/lang/returns/src/lib.rs @@ -15,5 +15,8 @@ use harness::Case; /// Each case's name and what it runs. The first two bytes, big-endian, pick one modulo /// their count, so a case added at the end moves no other. fn cases() -> Vec<(&'static str, Case)> { - vec![("returns::returns", returns::returns), ("returns::options", returns::options)] + vec![ + ("returns::returns", returns::returns), + ("returns::options", returns::options), + ] } diff --git a/ts/fixtures/lang/returns/src/returns.rs b/ts/fixtures/lang/returns/src/returns.rs index 9f5a8eff7..1f64d6141 100644 --- a/ts/fixtures/lang/returns/src/returns.rs +++ b/ts/fixtures/lang/returns/src/returns.rs @@ -311,7 +311,7 @@ fn numbers(kind: u8, data: &[u8], out: &mut Vec) { sum = sum.wrapping_mul(31).wrapping_add(n as u64); rest = &rest[n as usize + 1..]; } - out.extend_from_slice(&sum.to_be_bytes()); + sum.put(out); } pub fn returns(kind: u8, data: &[u8], out: &mut Vec) { @@ -512,11 +512,57 @@ fn opt_late(data: &[u8], other: &[u8]) -> Result, Error> { opt_u64(data) } +/// A number's bytes, as each case writes it. +trait Put { + fn put(self, out: &mut Vec); +} + +macro_rules! put { + ($($t:ty),*) => {$( + impl Put for $t { + fn put(self, out: &mut Vec) { + out.extend_from_slice(&self.to_be_bytes()); + } + } + )*}; +} + +put!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +impl Put for usize { + fn put(self, out: &mut Vec) { + (self as u64).put(out); + } +} + +impl Put for bool { + fn put(self, out: &mut Vec) { + out.push(self as u8); + } +} + +impl Put for char { + fn put(self, out: &mut Vec) { + (self as u32).put(out); + } +} + /// A float's bits, or its sign alone if it is a NaN, whose payload is the engine's. -fn put_float(out: &mut Vec, bits: u64, nan: bool, negative: bool) { - match nan { - true => out.extend_from_slice(&[0x7f, negative as u8]), - false => out.extend_from_slice(&bits.to_be_bytes()), +impl Put for f32 { + fn put(self, out: &mut Vec) { + match self.is_nan() { + true => out.extend_from_slice(&[0x7f, self.is_sign_negative() as u8]), + false => self.to_bits().put(out), + } + } +} + +impl Put for f64 { + fn put(self, out: &mut Vec) { + match self.is_nan() { + true => out.extend_from_slice(&[0x7f, self.is_sign_negative() as u8]), + false => self.to_bits().put(out), + } } } @@ -524,38 +570,39 @@ fn put_float(out: &mut Vec, bits: u64, nan: bool, negative: bool) { /// written again. pub fn options(kind: u8, data: &[u8], out: &mut Vec) { macro_rules! push { - ($r:expr, |$v:ident| $put:expr) => { + ($r:expr) => { match $r { - Ok(Some($v)) => { + Ok(Some(v)) => { out.push(1); - $put; + v.put(out); } Ok(None) => out.push(0), Err(e) => out.extend_from_slice(&[0xee, e.code()]), } }; } - push!(opt_u8(data), |v| out.push(v)); - push!(opt_u16(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_u32(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_u64(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_u128(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_usize(data), |v| out.extend_from_slice(&(v as u64).to_be_bytes())); - push!(opt_i8(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_i16(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_i32(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_i64(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_i128(data), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_f32(data), |v| put_float(out, v.to_bits().into(), v.is_nan(), v.is_sign_negative())); - push!(opt_f64(data), |v| put_float(out, v.to_bits(), v.is_nan(), v.is_sign_negative())); - push!(opt_bool(data), |v| out.push(v as u8)); + push!(opt_u8(data)); + push!(opt_u16(data)); + push!(opt_u32(data)); + push!(opt_u64(data)); + push!(opt_u128(data)); + push!(opt_usize(data)); + push!(opt_i8(data)); + push!(opt_i16(data)); + push!(opt_i32(data)); + push!(opt_i64(data)); + push!(opt_i128(data)); + push!(opt_f32(data)); + push!(opt_f64(data)); + push!(opt_bool(data)); match opt_char(data) { - Ok(Some(c)) => out.extend_from_slice(&(c as u32).to_be_bytes()), + Ok(Some(c)) => c.put(out), Ok(None) => out.push(0), Err(b) => out.extend_from_slice(&[0xee, b]), } - push!(opt_again(data, kind % 2 == 0), |v| out.extend_from_slice(&v.to_be_bytes())); - push!(opt_late(data, &data[data.len().min(1)..]), |v| out.extend_from_slice(&v.to_be_bytes())); + let tail = &data[data.len().min(1)..]; + push!(opt_again(data, kind % 2 == 0)); + push!(opt_late(data, tail)); match opt_first(data) { Ok((n, len)) => out.extend_from_slice(&[n.map_or(0xaa, |n| n), len as u8]), Err(e) => out.push(e.code()), @@ -563,7 +610,7 @@ pub fn options(kind: u8, data: &[u8], out: &mut Vec) { match opt_last(data) { Ok((len, n)) => { out.push(len as u8); - out.extend_from_slice(&n.unwrap_or(-1).to_be_bytes()); + n.unwrap_or(-1).put(out); } Err(e) => out.push(e.code()), } @@ -572,18 +619,22 @@ pub fn options(kind: u8, data: &[u8], out: &mut Vec) { Err(e) => out.push(e.code()), } // Both held while the slot is written again between them. - let (x, y) = (opt_u64(data), opt_u64(&data[data.len().min(1)..])); + let (x, y) = (opt_u64(data), opt_u64(tail)); match (x, y) { - (Ok(Some(x)), Ok(Some(y))) => out.extend_from_slice(&(x ^ y).to_be_bytes()), + (Ok(Some(x)), Ok(Some(y))) => (x ^ y).put(out), (x, y) => out.extend_from_slice(&[x.is_ok() as u8, y.is_ok() as u8, (x == y) as u8]), } // Kept whole: in a collection, and through a function as a value. let kept: Vec, Error>> = (0..3).map(|i| opt_u64(&data[data.len().min(i)..])).collect(); for r in kept { - push!(r, |v| out.extend_from_slice(&v.to_be_bytes())); + push!(r); } - let f: fn(&[u8]) -> Result, Error> = if kind % 2 == 0 { opt_i64 } else { |d| opt_i64(&d[d.len().min(1)..]) }; - push!(f(data), |v| out.extend_from_slice(&v.to_be_bytes())); + let f: fn(&[u8]) -> Result, Error> = if kind % 2 == 0 { + opt_i64 + } else { + |d| opt_i64(&d[d.len().min(1)..]) + }; + push!(f(data)); // A loop that calls again while the last result is still read. let mut rest = data; let mut sum = 0u32; @@ -591,5 +642,5 @@ pub fn options(kind: u8, data: &[u8], out: &mut Vec) { sum = sum.wrapping_mul(31).wrapping_add(n); rest = &rest[(n as usize % 5).min(rest.len()).max(1)..]; } - out.extend_from_slice(&sum.to_be_bytes()); + sum.put(out); }