pub const MAGIC_LEN: usize = 16;
pub const SCHEMA_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.schema\0\0\0";
pub const OPERATION_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.opcode\0\0\0";
pub const STRUCT_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.struct\0\0\0";
pub const FUNCTION_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.func\0\0\0\0\0";
pub const STRUCT_EXPORT_DESC_PREFIX: &str = "__wasmgc_struct_export_desc.";
pub const FUNCTION_BRIDGE_PREFIX: &str = "__wasmgc_function_bridge.";
pub const FUNCTION_DESC_PREFIX: &str = "__wasmgc_function_desc.";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Layout {
pub pointer_size: u32,
pub value_kind_size: u32,
pub value_kind_tag_offset: u32,
pub value_kind_target_offset: u32,
pub schema_size: u32,
pub schema_magic_offset: u32,
pub schema_kind_offset: u32,
pub schema_field_count_offset: u32,
pub schema_fields_offset: u32,
pub operation_size: u32,
pub operation_magic_offset: u32,
pub operation_function_offset: u32,
pub operation_schema_offset: u32,
pub operation_kind_offset: u32,
pub operation_field_index_offset: u32,
pub struct_export_size: u32,
pub struct_export_magic_offset: u32,
pub struct_export_schema_offset: u32,
pub struct_export_kind_offset: u32,
pub struct_export_field_offset: u32,
pub struct_export_name_offset: u32,
pub struct_export_name_len_offset: u32,
pub function_size: u32,
pub function_magic_offset: u32,
pub function_kind_offset: u32,
pub function_param_count_offset: u32,
pub function_params_offset: u32,
pub function_result_count_offset: u32,
pub function_module_len_offset: u32,
pub function_name_len_offset: u32,
}
impl Layout {
pub const WASM32: Self = Self {
pointer_size: 4,
value_kind_size: 8,
value_kind_tag_offset: 0,
value_kind_target_offset: 4,
schema_size: 24,
schema_magic_offset: 0,
schema_kind_offset: 16,
schema_field_count_offset: 20,
schema_fields_offset: 24,
operation_size: 32,
operation_magic_offset: 0,
operation_function_offset: 16,
operation_schema_offset: 20,
operation_kind_offset: 24,
operation_field_index_offset: 28,
struct_export_size: 32,
struct_export_magic_offset: 0,
struct_export_schema_offset: 16,
struct_export_kind_offset: 20,
struct_export_field_offset: 24,
struct_export_name_offset: 32,
struct_export_name_len_offset: 28,
function_size: 36,
function_magic_offset: 0,
function_kind_offset: 16,
function_param_count_offset: 20,
function_params_offset: 36,
function_result_count_offset: 24,
function_module_len_offset: 28,
function_name_len_offset: 32,
};
pub const WASM64: Self = Self {
pointer_size: 8,
value_kind_size: 16,
value_kind_tag_offset: 0,
value_kind_target_offset: 8,
schema_size: 24,
schema_magic_offset: 0,
schema_kind_offset: 16,
schema_field_count_offset: 20,
schema_fields_offset: 24,
operation_size: 40,
operation_magic_offset: 0,
operation_function_offset: 16,
operation_schema_offset: 24,
operation_kind_offset: 32,
operation_field_index_offset: 36,
struct_export_size: 40,
struct_export_magic_offset: 0,
struct_export_schema_offset: 16,
struct_export_kind_offset: 24,
struct_export_field_offset: 28,
struct_export_name_offset: 40,
struct_export_name_len_offset: 32,
function_size: 36,
function_magic_offset: 0,
function_kind_offset: 16,
function_param_count_offset: 20,
function_params_offset: 40,
function_result_count_offset: 24,
function_module_len_offset: 28,
function_name_len_offset: 32,
};
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ValueTag {
I32 = 1,
I64 = 2,
F32 = 3,
F64 = 4,
Bool = 5,
Ref = 6,
NullableRef = 7,
Array = 8,
NullableArray = 10,
ExternRef = 11,
I8 = 12,
I16 = 13,
}
impl ValueTag {
pub const fn from_raw(value: u32) -> Option<Self> {
match value {
1 => Some(Self::I32),
2 => Some(Self::I64),
3 => Some(Self::F32),
4 => Some(Self::F64),
5 => Some(Self::Bool),
6 => Some(Self::Ref),
7 => Some(Self::NullableRef),
8 => Some(Self::Array),
10 => Some(Self::NullableArray),
11 => Some(Self::ExternRef),
12 => Some(Self::I8),
13 => Some(Self::I16),
_ => None,
}
}
pub const fn is_reference(self) -> bool {
matches!(
self,
Self::Ref | Self::NullableRef | Self::Array | Self::NullableArray | Self::ExternRef
)
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum OperationKind {
StructNew = 0,
StructGet = 1,
StructSet = 2,
ArrayNew = 3,
ArrayNewFixed = 4,
ArrayNewDefault = 13,
ArrayLen = 5,
ArrayGet = 6,
ArraySet = 7,
ArrayFill = 8,
ArrayCopy = 9,
ArrayFromSlice = 10,
ArrayRead = 11,
ArrayWrite = 12,
ArrayReadAbi = 14,
ArrayWriteAbi = 15,
}
impl OperationKind {
pub const fn from_raw(value: u32) -> Option<Self> {
match value {
0 => Some(Self::StructNew),
1 => Some(Self::StructGet),
2 => Some(Self::StructSet),
3 => Some(Self::ArrayNew),
4 => Some(Self::ArrayNewFixed),
5 => Some(Self::ArrayLen),
6 => Some(Self::ArrayGet),
7 => Some(Self::ArraySet),
8 => Some(Self::ArrayFill),
9 => Some(Self::ArrayCopy),
10 => Some(Self::ArrayFromSlice),
11 => Some(Self::ArrayRead),
12 => Some(Self::ArrayWrite),
13 => Some(Self::ArrayNewDefault),
14 => Some(Self::ArrayReadAbi),
15 => Some(Self::ArrayWriteAbi),
_ => None,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ValueKind {
tag: ValueTag,
target: *const Schema,
}
impl ValueKind {
pub const I8: Self = Self::scalar(ValueTag::I8);
pub const I16: Self = Self::scalar(ValueTag::I16);
pub const I32: Self = Self::scalar(ValueTag::I32);
pub const I64: Self = Self::scalar(ValueTag::I64);
pub const F32: Self = Self::scalar(ValueTag::F32);
pub const F64: Self = Self::scalar(ValueTag::F64);
pub const BOOL: Self = Self::scalar(ValueTag::Bool);
pub const EXTERNREF: Self = Self::scalar(ValueTag::ExternRef);
const fn scalar(tag: ValueTag) -> Self {
Self {
tag,
target: core::ptr::null(),
}
}
pub const fn is_defaultable(self) -> bool {
matches!(
self.tag,
ValueTag::I8
| ValueTag::I16
| ValueTag::I32
| ValueTag::I64
| ValueTag::F32
| ValueTag::F64
| ValueTag::Bool
| ValueTag::NullableRef
| ValueTag::NullableArray
| ValueTag::ExternRef
)
}
pub const fn reference(target: *const Schema, nullable: bool) -> Self {
Self {
tag: if nullable {
ValueTag::NullableRef
} else {
ValueTag::Ref
},
target,
}
}
pub const fn array(target: *const Schema, nullable: bool) -> Self {
Self {
tag: if nullable {
ValueTag::NullableArray
} else {
ValueTag::Array
},
target,
}
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SchemaKind {
Struct = 0,
Array = 1,
}
impl SchemaKind {
pub const fn from_raw(value: u32) -> Option<Self> {
match value {
0 => Some(Self::Struct),
1 => Some(Self::Array),
_ => None,
}
}
}
#[repr(C)]
pub struct Schema {
pub magic: [u8; MAGIC_LEN],
pub kind: SchemaKind,
pub field_count: u32,
}
#[repr(C)]
pub struct SchemaData<const N: usize> {
pub header: Schema,
pub fields: [ValueKind; N],
}
impl<const N: usize> SchemaData<N> {
pub const fn new(fields: [ValueKind; N]) -> Self {
assert!(N <= u32::MAX as usize);
Self {
header: Schema {
magic: SCHEMA_MAGIC,
kind: SchemaKind::Struct,
field_count: N as u32,
},
fields,
}
}
}
impl SchemaData<1> {
pub const fn array(element: ValueKind) -> Self {
Self {
header: Schema {
magic: SCHEMA_MAGIC,
kind: SchemaKind::Array,
field_count: 1,
},
fields: [element],
}
}
}
#[repr(C)]
pub struct Operation {
pub magic: [u8; MAGIC_LEN],
pub function: *const (),
pub schema: *const Schema,
pub kind: OperationKind,
pub field_index: u32,
}
impl Operation {
pub const fn new(
schema: *const Schema,
kind: OperationKind,
field_index: u32,
function: *const (),
) -> Self {
Self {
magic: OPERATION_MAGIC,
function,
schema,
kind,
field_index,
}
}
}
#[repr(C)]
pub struct Struct {
pub magic: [u8; MAGIC_LEN],
pub schema: *const Schema,
pub kind: OperationKind,
pub field: u32,
pub name_len: u32,
}
#[repr(C)]
pub struct StructData<const N: usize> {
pub header: Struct,
pub name: [u8; N],
}
impl<const N: usize> StructData<N> {
pub const fn new(
schema: *const Schema,
kind: OperationKind,
field: u32,
name: [u8; N],
) -> Self {
assert!(N <= u32::MAX as usize);
Self {
header: Struct {
magic: STRUCT_MAGIC,
schema,
kind,
field,
name_len: N as u32,
},
name,
}
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FunctionKind {
Export = 0,
Import = 1,
}
impl FunctionKind {
pub const fn from_raw(value: u32) -> Option<Self> {
match value {
0 => Some(Self::Export),
1 => Some(Self::Import),
_ => None,
}
}
}
#[repr(C)]
pub struct Function {
pub magic: [u8; MAGIC_LEN],
pub kind: FunctionKind,
pub param_count: u32,
pub result_count: u32,
pub module_len: u32,
pub name_len: u32,
}
#[repr(C)]
pub struct FunctionData<const P: usize, const R: usize, const M: usize, const N: usize> {
pub header: Function,
pub params: [ValueKind; P],
pub result: [ValueKind; R],
pub module: [u8; M],
pub name: [u8; N],
}
impl<const P: usize, const R: usize, const M: usize, const N: usize> FunctionData<P, R, M, N> {
pub const fn new(
kind: FunctionKind,
params: [ValueKind; P],
result: [ValueKind; R],
module: [u8; M],
name: [u8; N],
) -> Self {
assert!(P <= u32::MAX as usize);
assert!(R <= 1);
assert!(M <= u32::MAX as usize);
assert!(N <= u32::MAX as usize);
Self {
header: Function {
magic: FUNCTION_MAGIC,
kind,
param_count: P as u32,
result_count: R as u32,
module_len: M as u32,
name_len: N as u32,
},
params,
result,
module,
name,
}
}
}
unsafe impl Sync for ValueKind {}
unsafe impl Sync for Operation {}
unsafe impl Sync for Struct {}
const _: () = {
use core::mem::{align_of, offset_of, size_of};
const fn align_up(size: usize, alignment: usize) -> usize {
size.next_multiple_of(alignment)
}
let layout = match size_of::<*const ()>() {
4 => Layout::WASM32,
8 => Layout::WASM64,
_ => panic!("unsupported descriptor pointer width"),
};
assert!(size_of::<extern "C" fn()>() == layout.pointer_size as usize);
assert!(size_of::<ValueKind>() == layout.value_kind_size as usize);
assert!(offset_of!(ValueKind, target) == layout.value_kind_target_offset as usize);
assert!(size_of::<Schema>() == layout.schema_size as usize);
assert!(offset_of!(Schema, magic) == layout.schema_magic_offset as usize);
assert!(offset_of!(Schema, kind) == layout.schema_kind_offset as usize);
assert!(offset_of!(Schema, field_count) == layout.schema_field_count_offset as usize);
assert!(offset_of!(SchemaData<0>, header) == 0);
assert!(offset_of!(SchemaData<0>, fields) == layout.schema_fields_offset as usize);
assert!(offset_of!(SchemaData<3>, fields) == layout.schema_fields_offset as usize);
assert!(size_of::<SchemaData<0>>() == layout.schema_fields_offset as usize);
assert!(
size_of::<SchemaData<3>>()
== layout.schema_fields_offset as usize + 3 * layout.value_kind_size as usize
);
assert!(size_of::<Operation>() == layout.operation_size as usize);
assert!(offset_of!(Operation, function) == layout.operation_function_offset as usize);
assert!(size_of::<Struct>() == layout.struct_export_size as usize);
assert!(offset_of!(Struct, magic) == layout.struct_export_magic_offset as usize);
assert!(offset_of!(Struct, schema) == layout.struct_export_schema_offset as usize);
assert!(offset_of!(Struct, kind) == layout.struct_export_kind_offset as usize);
assert!(offset_of!(Struct, field) == layout.struct_export_field_offset as usize);
assert!(offset_of!(Struct, name_len) == layout.struct_export_name_len_offset as usize);
assert!(offset_of!(StructData<0>, header) == 0);
assert!(offset_of!(StructData<0>, name) == layout.struct_export_name_offset as usize);
assert!(offset_of!(StructData<5>, name) == layout.struct_export_name_offset as usize);
assert!(size_of::<StructData<0>>() == layout.struct_export_size as usize);
assert!(
size_of::<StructData<5>>()
== align_up(
layout.struct_export_name_offset as usize + 5,
align_of::<StructData<5>>()
)
);
assert!(size_of::<Function>() == layout.function_size as usize);
assert!(offset_of!(Function, magic) == layout.function_magic_offset as usize);
assert!(offset_of!(Function, kind) == layout.function_kind_offset as usize);
assert!(offset_of!(Function, param_count) == layout.function_param_count_offset as usize);
assert!(offset_of!(Function, result_count) == layout.function_result_count_offset as usize);
assert!(offset_of!(Function, module_len) == layout.function_module_len_offset as usize);
assert!(offset_of!(Function, name_len) == layout.function_name_len_offset as usize);
type EmptyFunction = FunctionData<0, 0, 0, 0>;
type NamedFunction = FunctionData<2, 1, 3, 5>;
type VoidFunction = FunctionData<1, 0, 0, 1>;
let params = layout.function_params_offset as usize;
let value_size = layout.value_kind_size as usize;
assert!(offset_of!(EmptyFunction, header) == 0);
assert!(offset_of!(EmptyFunction, params) == params);
assert!(offset_of!(EmptyFunction, result) == params);
assert!(offset_of!(EmptyFunction, module) == params);
assert!(offset_of!(EmptyFunction, name) == params);
assert!(size_of::<EmptyFunction>() == params);
assert!(offset_of!(NamedFunction, params) == params);
assert!(offset_of!(NamedFunction, result) == params + 2 * value_size);
assert!(offset_of!(NamedFunction, module) == params + 3 * value_size);
assert!(offset_of!(NamedFunction, name) == params + 3 * value_size + 3);
assert!(
size_of::<NamedFunction>()
== align_up(params + 3 * value_size + 3 + 5, align_of::<NamedFunction>())
);
assert!(offset_of!(VoidFunction, result) == params + value_size);
assert!(offset_of!(VoidFunction, module) == params + value_size);
assert!(offset_of!(VoidFunction, name) == params + value_size);
assert!(
size_of::<VoidFunction>() == align_up(params + value_size + 1, align_of::<VoidFunction>())
);
};