Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
512 changes: 272 additions & 240 deletions build.rs

Large diffs are not rendered by default.

19 changes: 14 additions & 5 deletions docs/callable-runtime.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
# Script call frames and callable values

RustScript bytecode format version 9 introduces runtime script call frames and first-class callable values.
RustScript bytecode format version 11 (VMBC v11) introduces runtime script call frames, first-class callable values, and the static builtin ID catalog.

## Bytecode contract

- `call <import:u16> <argc:u8>` remains the direct host/builtin operation.
- `call <import:u16> <argc:u8>` remains the direct host/builtin operation; the `u16` operand is an explicit static builtin call index from the catalog (or a host-import slot) — never a count-derived offset.
- `callvalue <argc:u8>` consumes a stack segment in `callee, arg0, ..., argN` order.
- callable environments are bound through the internal builtin call path; callable creation adds no bytecode opcode.
- `ret` completes the active script frame. A nested frame leaves exactly one result at the caller segment base, using `null` when the body produced no value. Root `ret` keeps the historical program-result stack behavior.

VMBC v9 is a hard format boundary. Decoders reject older versions. The stream includes script-function entry ranges, callable prototypes, function regions, and root callable bindings. PDRC v4 recordings and AOT artifacts use their corresponding bumped format/ABI versions and include callable metadata in cache identity.
VMBC v11 is a hard format boundary. Decoders reject all earlier versions (v10 and below) with a deterministic unsupported-version error; there is no compatibility decoder and no old-ID alias. The stream includes script-function entry ranges, callable prototypes, function regions, root callable bindings, and call indices drawn from the static builtin catalog. PDRC v6 recordings and AOT artifacts (format 7, ABI 6) use their corresponding bumped versions and include callable metadata in cache identity.

## Static builtin IDs

Every VM-visible builtin (ordinary, internal, and special-call) has one explicit, immutable `u16` call index assigned in `src/builtins/catalog.rs`. `build.rs` parses that catalog and generates the `BuiltinFunction` enum discriminants, `call_index`/`from_call_index`, the `builtin_call_index` reverse lookup, and the `BUILTIN_CATALOG` iteration from the explicit IDs; no ID is derived from catalog length or source order.

- **Immutable explicit IDs.** IDs never change once assigned. Adding or reordering catalog entries never renumbers existing entries; new builtins take the next free ID in their documented block (extension `0x0000..=0xFF8F` for future builtins and host imports, special-call `0xFF90..=0xFFA1`, ordinary `0xFFA2..=0xFFFF`). The reserved sentinel gap `0xFF90..=0xFF92` stays unassigned.
- **Build-time validation.** The build fails on duplicate IDs, duplicate source names, duplicate Rust variants, out-of-block IDs, class/gate inconsistencies, a discovered runtime callable without an explicit ID, or a catalog entry without a runtime callable.
- **Shared std/no-std IDs.** `pd-vm-nostd` dispatches on the same static indices through the checked-in generated mirror `pd-vm-nostd/src/generated_builtin_ids.rs`; the workspace test `static_builtin_ids_are_frozen` fails when the mirror drifts from the catalog.
- **One-time format break.** The static ID migration bumped VMBC to v11 (and the internal bytecode ABI to 11). Older VMBC versions are rejected, never decoded.

## Runtime model

Expand All @@ -36,8 +45,8 @@ PDRC recordings preserve full execution-frame metadata. Callable environments us

## Optimized backends

Whole-program AOT and Trace JIT use the same builtin call path for environment binding and native frame dispatch for `callvalue`. Script-frame entry and return preserve frame-relative locals and typed continuations.
Whole-program AOT and Trace JIT use the same builtin call path (static catalog IDs) for environment binding and native frame dispatch for `callvalue`. Script-frame entry and return preserve frame-relative locals and typed continuations.

## Embedded runtime

`pd-vm-nostd` decodes the same VMBC v9 callable metadata and executes callable binding, `callvalue`, recursive frames, captures, and direct host targets using `core` plus `alloc`.
`pd-vm-nostd` decodes the same VMBC v11 callable metadata and executes callable binding, `callvalue`, recursive frames, captures, and direct host targets using `core` plus `alloc`, dispatching on the identical static builtin IDs via its checked-in generated mirror.
2 changes: 1 addition & 1 deletion pd-vm-nostd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compiler, parser, CLI, debugger, JIT/AOT backends, filesystem support, and opera

## Runtime surface

- VMBC v9 decoding with script-call and callable metadata
- VMBC v11 decoding with script-call and callable metadata
- stack, local, and recursive script-frame execution for direct bytecode opcodes
- instruction fuel with pause/resume support
- synchronous named host bindings and dynamic host dispatch
Expand Down
222 changes: 222 additions & 0 deletions pd-vm-nostd/src/generated_builtin_ids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// @generated mirror of src/builtins/catalog.rs (static builtin IDs).
//
// Every VM-visible builtin has one explicit u16 call index assigned in the
// authoritative catalog at the workspace root. This file mirrors those IDs so
// pd-vm-nostd dispatches on the same static indices without a build script.
// The workspace test `static_builtin_ids_are_frozen` fails when this file
// drifts from the catalog; do not edit by hand.

#![allow(dead_code)]

pub const DETACH_LOCAL_CALL_INDEX: u16 = 0xFF93;
pub const BIND_CALLABLE_CALL_INDEX: u16 = 0xFF94;
pub const MAP_ITER_CLOSE_CALL_INDEX: u16 = 0xFF95;
pub const MAP_ITER_TAKE_VALUE_CALL_INDEX: u16 = 0xFF96;
pub const MAP_ITER_TAKE_KEY_CALL_INDEX: u16 = 0xFF97;
pub const MAP_ITER_NEXT_CALL_INDEX: u16 = 0xFF98;
pub const MAP_ITER_INIT_CALL_INDEX: u16 = 0xFF99;
pub const STRING_SPLIT_LITERAL_CALL_INDEX: u16 = 0xFF9A;
pub const STRING_CONTAINS_CALL_INDEX: u16 = 0xFF9B;
pub const STRING_REPLACE_LITERAL_CALL_INDEX: u16 = 0xFF9C;
pub const STRING_LOWER_ASCII_CALL_INDEX: u16 = 0xFF9D;
pub const FORMAT_TEMPLATE_CALL_INDEX: u16 = 0xFF9E;
pub const TO_STRING_CALL_INDEX: u16 = 0xFF9F;
pub const TYPE_CALL_INDEX: u16 = 0xFFA0;
pub const ASSERT_CALL_INDEX: u16 = 0xFFA1;
pub const LEN_CALL_INDEX: u16 = 0xFFA2;
pub const SLICE_CALL_INDEX: u16 = 0xFFA3;
pub const CONCAT_CALL_INDEX: u16 = 0xFFA4;
pub const ARRAY_NEW_CALL_INDEX: u16 = 0xFFA5;
pub const ARRAY_PUSH_CALL_INDEX: u16 = 0xFFA6;
pub const MAP_NEW_CALL_INDEX: u16 = 0xFFA7;
pub const GET_CALL_INDEX: u16 = 0xFFA8;
pub const HAS_CALL_INDEX: u16 = 0xFFA9;
pub const SET_CALL_INDEX: u16 = 0xFFAA;
pub const KEYS_CALL_INDEX: u16 = 0xFFAB;
pub const BYTES_FROM_UTF8_CALL_INDEX: u16 = 0xFFAC;
pub const BYTES_TO_UTF8_CALL_INDEX: u16 = 0xFFAD;
pub const BYTES_TO_UTF8_LOSSY_CALL_INDEX: u16 = 0xFFAE;
pub const BYTES_FROM_HEX_CALL_INDEX: u16 = 0xFFAF;
pub const BYTES_TO_HEX_CALL_INDEX: u16 = 0xFFB0;
pub const BYTES_FROM_BASE64_CALL_INDEX: u16 = 0xFFB1;
pub const BYTES_TO_BASE64_CALL_INDEX: u16 = 0xFFB2;
pub const BYTES_FROM_ARRAY_U8_CALL_INDEX: u16 = 0xFFB3;
pub const BYTES_TO_ARRAY_U8_CALL_INDEX: u16 = 0xFFB4;
pub const IO_OPEN_CALL_INDEX: u16 = 0xFFB5;
pub const IO_POPEN_CALL_INDEX: u16 = 0xFFB6;
pub const IO_READ_ALL_CALL_INDEX: u16 = 0xFFB7;
pub const IO_READ_LINE_CALL_INDEX: u16 = 0xFFB8;
pub const IO_WRITE_CALL_INDEX: u16 = 0xFFB9;
pub const IO_FLUSH_CALL_INDEX: u16 = 0xFFBA;
pub const IO_CLOSE_CALL_INDEX: u16 = 0xFFBB;
pub const IO_EXISTS_CALL_INDEX: u16 = 0xFFBC;
pub const RE_MATCH_CALL_INDEX: u16 = 0xFFBD;
pub const RE_FIND_CALL_INDEX: u16 = 0xFFBE;
pub const RE_REPLACE_CALL_INDEX: u16 = 0xFFBF;
pub const RE_SPLIT_CALL_INDEX: u16 = 0xFFC0;
pub const RE_CAPTURES_CALL_INDEX: u16 = 0xFFC1;
pub const JSON_ENCODE_CALL_INDEX: u16 = 0xFFC2;
pub const JSON_DECODE_CALL_INDEX: u16 = 0xFFC4;
pub const JIT_SET_CONFIG_CALL_INDEX: u16 = 0xFFC5;
pub const JIT_GET_CONFIG_CALL_INDEX: u16 = 0xFFC6;
pub const JIT_SET_ENABLED_CALL_INDEX: u16 = 0xFFC7;
pub const JIT_GET_ENABLED_CALL_INDEX: u16 = 0xFFC8;
pub const JIT_SET_HOT_LOOP_THRESHOLD_CALL_INDEX: u16 = 0xFFC9;
pub const JIT_GET_HOT_LOOP_THRESHOLD_CALL_INDEX: u16 = 0xFFCA;
pub const JIT_SET_MAX_TRACE_LEN_CALL_INDEX: u16 = 0xFFCB;
pub const JIT_GET_MAX_TRACE_LEN_CALL_INDEX: u16 = 0xFFCC;
pub const MATH_PI_CALL_INDEX: u16 = 0xFFCD;
pub const MATH_TAU_CALL_INDEX: u16 = 0xFFCE;
pub const MATH_E_CALL_INDEX: u16 = 0xFFCF;
pub const MATH_EPSILON_CALL_INDEX: u16 = 0xFFD0;
pub const MATH_INF_CALL_INDEX: u16 = 0xFFD1;
pub const MATH_NEG_INF_CALL_INDEX: u16 = 0xFFD2;
pub const MATH_NAN_CALL_INDEX: u16 = 0xFFD3;
pub const MATH_ABS_CALL_INDEX: u16 = 0xFFD4;
pub const MATH_SQRT_CALL_INDEX: u16 = 0xFFD5;
pub const MATH_CBRT_CALL_INDEX: u16 = 0xFFD6;
pub const MATH_EXP_CALL_INDEX: u16 = 0xFFD7;
pub const MATH_EXP2_CALL_INDEX: u16 = 0xFFD8;
pub const MATH_LN_CALL_INDEX: u16 = 0xFFD9;
pub const MATH_LN_1P_CALL_INDEX: u16 = 0xFFDA;
pub const MATH_LOG2_CALL_INDEX: u16 = 0xFFDB;
pub const MATH_LOG10_CALL_INDEX: u16 = 0xFFDC;
pub const MATH_SIN_CALL_INDEX: u16 = 0xFFDD;
pub const MATH_COS_CALL_INDEX: u16 = 0xFFDE;
pub const MATH_TAN_CALL_INDEX: u16 = 0xFFDF;
pub const MATH_ASIN_CALL_INDEX: u16 = 0xFFE0;
pub const MATH_ACOS_CALL_INDEX: u16 = 0xFFE1;
pub const MATH_ATAN_CALL_INDEX: u16 = 0xFFE2;
pub const MATH_SINH_CALL_INDEX: u16 = 0xFFE3;
pub const MATH_COSH_CALL_INDEX: u16 = 0xFFE4;
pub const MATH_TANH_CALL_INDEX: u16 = 0xFFE5;
pub const MATH_FLOOR_CALL_INDEX: u16 = 0xFFE6;
pub const MATH_CEIL_CALL_INDEX: u16 = 0xFFE7;
pub const MATH_ROUND_CALL_INDEX: u16 = 0xFFE8;
pub const MATH_TRUNC_CALL_INDEX: u16 = 0xFFE9;
pub const MATH_FRACT_CALL_INDEX: u16 = 0xFFEA;
pub const MATH_SIGNUM_CALL_INDEX: u16 = 0xFFEB;
pub const MATH_TO_DEGREES_CALL_INDEX: u16 = 0xFFEC;
pub const MATH_TO_RADIANS_CALL_INDEX: u16 = 0xFFED;
pub const MATH_IS_NAN_CALL_INDEX: u16 = 0xFFEE;
pub const MATH_IS_INFINITE_CALL_INDEX: u16 = 0xFFEF;
pub const MATH_IS_FINITE_CALL_INDEX: u16 = 0xFFF0;
pub const MATH_ATAN2_CALL_INDEX: u16 = 0xFFF1;
pub const MATH_POWF_CALL_INDEX: u16 = 0xFFF2;
pub const MATH_POWI_CALL_INDEX: u16 = 0xFFF3;
pub const MATH_HYPOT_CALL_INDEX: u16 = 0xFFF4;
pub const MATH_LOG_CALL_INDEX: u16 = 0xFFF5;
pub const MATH_MIN_CALL_INDEX: u16 = 0xFFF6;
pub const MATH_MAX_CALL_INDEX: u16 = 0xFFF7;
pub const MATH_COPYSIGN_CALL_INDEX: u16 = 0xFFF8;
pub const MATH_CLAMP_CALL_INDEX: u16 = 0xFFF9;
pub const MATH_MUL_ADD_CALL_INDEX: u16 = 0xFFFA;
pub const COUNT_CALL_INDEX: u16 = 0xFFFB;

/// Every static builtin call index, ascending.
pub const ALL_CALL_INDICES: &[u16] = &[
DETACH_LOCAL_CALL_INDEX,
BIND_CALLABLE_CALL_INDEX,
MAP_ITER_CLOSE_CALL_INDEX,
MAP_ITER_TAKE_VALUE_CALL_INDEX,
MAP_ITER_TAKE_KEY_CALL_INDEX,
MAP_ITER_NEXT_CALL_INDEX,
MAP_ITER_INIT_CALL_INDEX,
STRING_SPLIT_LITERAL_CALL_INDEX,
STRING_CONTAINS_CALL_INDEX,
STRING_REPLACE_LITERAL_CALL_INDEX,
STRING_LOWER_ASCII_CALL_INDEX,
FORMAT_TEMPLATE_CALL_INDEX,
TO_STRING_CALL_INDEX,
TYPE_CALL_INDEX,
ASSERT_CALL_INDEX,
LEN_CALL_INDEX,
SLICE_CALL_INDEX,
CONCAT_CALL_INDEX,
ARRAY_NEW_CALL_INDEX,
ARRAY_PUSH_CALL_INDEX,
MAP_NEW_CALL_INDEX,
GET_CALL_INDEX,
HAS_CALL_INDEX,
SET_CALL_INDEX,
KEYS_CALL_INDEX,
BYTES_FROM_UTF8_CALL_INDEX,
BYTES_TO_UTF8_CALL_INDEX,
BYTES_TO_UTF8_LOSSY_CALL_INDEX,
BYTES_FROM_HEX_CALL_INDEX,
BYTES_TO_HEX_CALL_INDEX,
BYTES_FROM_BASE64_CALL_INDEX,
BYTES_TO_BASE64_CALL_INDEX,
BYTES_FROM_ARRAY_U8_CALL_INDEX,
BYTES_TO_ARRAY_U8_CALL_INDEX,
IO_OPEN_CALL_INDEX,
IO_POPEN_CALL_INDEX,
IO_READ_ALL_CALL_INDEX,
IO_READ_LINE_CALL_INDEX,
IO_WRITE_CALL_INDEX,
IO_FLUSH_CALL_INDEX,
IO_CLOSE_CALL_INDEX,
IO_EXISTS_CALL_INDEX,
RE_MATCH_CALL_INDEX,
RE_FIND_CALL_INDEX,
RE_REPLACE_CALL_INDEX,
RE_SPLIT_CALL_INDEX,
RE_CAPTURES_CALL_INDEX,
JSON_ENCODE_CALL_INDEX,
JSON_DECODE_CALL_INDEX,
JIT_SET_CONFIG_CALL_INDEX,
JIT_GET_CONFIG_CALL_INDEX,
JIT_SET_ENABLED_CALL_INDEX,
JIT_GET_ENABLED_CALL_INDEX,
JIT_SET_HOT_LOOP_THRESHOLD_CALL_INDEX,
JIT_GET_HOT_LOOP_THRESHOLD_CALL_INDEX,
JIT_SET_MAX_TRACE_LEN_CALL_INDEX,
JIT_GET_MAX_TRACE_LEN_CALL_INDEX,
MATH_PI_CALL_INDEX,
MATH_TAU_CALL_INDEX,
MATH_E_CALL_INDEX,
MATH_EPSILON_CALL_INDEX,
MATH_INF_CALL_INDEX,
MATH_NEG_INF_CALL_INDEX,
MATH_NAN_CALL_INDEX,
MATH_ABS_CALL_INDEX,
MATH_SQRT_CALL_INDEX,
MATH_CBRT_CALL_INDEX,
MATH_EXP_CALL_INDEX,
MATH_EXP2_CALL_INDEX,
MATH_LN_CALL_INDEX,
MATH_LN_1P_CALL_INDEX,
MATH_LOG2_CALL_INDEX,
MATH_LOG10_CALL_INDEX,
MATH_SIN_CALL_INDEX,
MATH_COS_CALL_INDEX,
MATH_TAN_CALL_INDEX,
MATH_ASIN_CALL_INDEX,
MATH_ACOS_CALL_INDEX,
MATH_ATAN_CALL_INDEX,
MATH_SINH_CALL_INDEX,
MATH_COSH_CALL_INDEX,
MATH_TANH_CALL_INDEX,
MATH_FLOOR_CALL_INDEX,
MATH_CEIL_CALL_INDEX,
MATH_ROUND_CALL_INDEX,
MATH_TRUNC_CALL_INDEX,
MATH_FRACT_CALL_INDEX,
MATH_SIGNUM_CALL_INDEX,
MATH_TO_DEGREES_CALL_INDEX,
MATH_TO_RADIANS_CALL_INDEX,
MATH_IS_NAN_CALL_INDEX,
MATH_IS_INFINITE_CALL_INDEX,
MATH_IS_FINITE_CALL_INDEX,
MATH_ATAN2_CALL_INDEX,
MATH_POWF_CALL_INDEX,
MATH_POWI_CALL_INDEX,
MATH_HYPOT_CALL_INDEX,
MATH_LOG_CALL_INDEX,
MATH_MIN_CALL_INDEX,
MATH_MAX_CALL_INDEX,
MATH_COPYSIGN_CALL_INDEX,
MATH_CLAMP_CALL_INDEX,
MATH_MUL_ADD_CALL_INDEX,
COUNT_CALL_INDEX,
];
1 change: 1 addition & 0 deletions pd-vm-nostd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
extern crate alloc;

mod error;
mod generated_builtin_ids;
mod host;
mod program;
mod value;
Expand Down
58 changes: 45 additions & 13 deletions pd-vm-nostd/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,23 +610,55 @@ impl<C> Vm<C> {
}

fn call_core_builtin(&mut self, index: u16, arity: u8) -> Option<VmResult<()>> {
const BUILTIN_BASE: u16 = 0xFFA3;
const ARRAY_NEW: u16 = BUILTIN_BASE + 3;
const ARRAY_PUSH: u16 = BUILTIN_BASE + 4;
const MAP_NEW: u16 = BUILTIN_BASE + 5;
const SET: u16 = BUILTIN_BASE + 8;
const BIND_CALLABLE: u16 = BUILTIN_BASE - 14;
const DETACH_LOCAL: u16 = BUILTIN_BASE - 15;
use super::generated_builtin_ids::{
ARRAY_NEW_CALL_INDEX, ARRAY_PUSH_CALL_INDEX, BIND_CALLABLE_CALL_INDEX,
CONCAT_CALL_INDEX, DETACH_LOCAL_CALL_INDEX, MAP_NEW_CALL_INDEX, SET_CALL_INDEX,
};

Some(match index {
ARRAY_NEW => {
CONCAT_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("concat", arity, 2) {
return Some(Err(error));
}
let start = match self.stack.len().checked_sub(2) {
Some(start) => start,
None => return Some(Err(VmError::StackUnderflow)),
};
let lhs = self.stack[start].clone();
let rhs = self.stack[start + 1].clone();
let result = match (lhs, rhs) {
(Value::String(lhs), Value::String(rhs)) => {
let mut value = String::with_capacity(lhs.len() + rhs.len());
value.push_str(&lhs);
value.push_str(&rhs);
Value::string(value)
}
(Value::Bytes(lhs), Value::Bytes(rhs)) => {
let mut value = Vec::with_capacity(lhs.len() + rhs.len());
value.extend_from_slice(&lhs);
value.extend_from_slice(&rhs);
Value::bytes(value)
}
(Value::Array(lhs), Value::Array(rhs)) => {
let mut value = Vec::with_capacity(lhs.len() + rhs.len());
value.extend_from_slice(&lhs);
value.extend_from_slice(&rhs);
Value::array(value)
}
_ => return Some(Err(VmError::TypeMismatch("concat operands"))),
};
self.stack.truncate(start);
self.stack.push(result);
Ok(())
}
ARRAY_NEW_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("array_new", arity, 0) {
return Some(Err(error));
}
self.stack.push(Value::array(Vec::new()));
Ok(())
}
ARRAY_PUSH => {
ARRAY_PUSH_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("array_push", arity, 2) {
return Some(Err(error));
}
Expand All @@ -643,14 +675,14 @@ impl<C> Vm<C> {
self.stack.push(Value::Array(values));
Ok(())
}
MAP_NEW => {
MAP_NEW_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("map_new", arity, 0) {
return Some(Err(error));
}
self.stack.push(Value::map(Vec::new()));
Ok(())
}
SET => {
SET_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("set", arity, 3) {
return Some(Err(error));
}
Expand All @@ -674,7 +706,7 @@ impl<C> Vm<C> {
self.stack.push(Value::Map(entries));
Ok(())
}
DETACH_LOCAL => {
DETACH_LOCAL_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("__detach_local", arity, 1) {
return Some(Err(error));
}
Expand All @@ -698,7 +730,7 @@ impl<C> Vm<C> {
self.stack.truncate(start);
Ok(())
}
BIND_CALLABLE => {
BIND_CALLABLE_CALL_INDEX => {
if let Err(error) = self.require_builtin_arity("__bind_callable", arity, 2) {
return Some(Err(error));
}
Expand Down
4 changes: 2 additions & 2 deletions pd-vm-nostd/src/vmbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::{
};

const MAGIC: [u8; 4] = *b"VMBC";
const VERSION_V10: u16 = 10;
const VERSION_V11: u16 = 11;
const FLAGS: u16 = 0;
const MAX_SCHEMA_DEPTH: usize = 64;
const MAX_CONSTANT_DEPTH: usize = 64;
Expand Down Expand Up @@ -57,7 +57,7 @@ pub fn decode_program(bytes: &[u8]) -> Result<Program, WireError> {
}

let version = cursor.read_u16()?;
if version != VERSION_V10 {
if version != VERSION_V11 {
return Err(WireError::UnsupportedVersion(version));
}
let flags = cursor.read_u16()?;
Expand Down
Loading
Loading