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 EXPORT_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.export\0\0\0";
pub const FUNCTION_MAGIC: [u8; MAGIC_LEN] = *b"wasmgc.func\0\0\0\0\0";
pub const EXPORT_PREFIX: &str = "__wasmgc_export.";
pub const FUNCTION_PREFIX: &str = "__wasmgc_function.";
pub const SIGNATURE_PREFIX: &str = "__wasmgc_signature.";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Layout {
pub pointer_size: u32,
pub field_kind_size: u32,
pub field_kind_tag_offset: u32,
pub field_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_offset: u32,
pub export_size: u32,
pub export_magic_offset: u32,
pub export_schema_offset: u32,
pub export_kind_offset: u32,
pub export_field_offset: u32,
pub export_name_offset: u32,
pub 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_offset: u32,
pub function_module_offset: u32,
pub function_module_len_offset: u32,
pub function_name_offset: u32,
pub function_name_len_offset: u32,
}
impl Layout {
pub const WASM32: Self = Self {
pointer_size: 4,
field_kind_size: 8,
field_kind_tag_offset: 0,
field_kind_target_offset: 4,
schema_size: 28,
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_offset: 28,
export_size: 36,
export_magic_offset: 0,
export_schema_offset: 16,
export_kind_offset: 20,
export_field_offset: 24,
export_name_offset: 28,
export_name_len_offset: 32,
function_size: 48,
function_magic_offset: 0,
function_kind_offset: 16,
function_param_count_offset: 20,
function_params_offset: 24,
function_result_offset: 28,
function_module_offset: 32,
function_module_len_offset: 36,
function_name_offset: 40,
function_name_len_offset: 44,
};
pub const WASM64: Self = Self {
pointer_size: 8,
field_kind_size: 16,
field_kind_tag_offset: 0,
field_kind_target_offset: 8,
schema_size: 32,
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_offset: 36,
export_size: 48,
export_magic_offset: 0,
export_schema_offset: 16,
export_kind_offset: 24,
export_field_offset: 28,
export_name_offset: 32,
export_name_len_offset: 40,
function_size: 72,
function_magic_offset: 0,
function_kind_offset: 16,
function_param_count_offset: 20,
function_params_offset: 24,
function_result_offset: 32,
function_module_offset: 40,
function_module_len_offset: 48,
function_name_offset: 56,
function_name_len_offset: 64,
};
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FieldTag {
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 FieldTag {
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 FieldKind {
tag: FieldTag,
target: *const Schema,
}
impl FieldKind {
pub const I8: Self = Self::scalar(FieldTag::I8);
pub const I16: Self = Self::scalar(FieldTag::I16);
pub const I32: Self = Self::scalar(FieldTag::I32);
pub const I64: Self = Self::scalar(FieldTag::I64);
pub const F32: Self = Self::scalar(FieldTag::F32);
pub const F64: Self = Self::scalar(FieldTag::F64);
pub const BOOL: Self = Self::scalar(FieldTag::Bool);
pub const EXTERNREF: Self = Self::scalar(FieldTag::ExternRef);
const fn scalar(tag: FieldTag) -> Self {
Self {
tag,
target: core::ptr::null(),
}
}
pub const fn is_defaultable(self) -> bool {
matches!(
self.tag,
FieldTag::I8
| FieldTag::I16
| FieldTag::I32
| FieldTag::I64
| FieldTag::F32
| FieldTag::F64
| FieldTag::Bool
| FieldTag::NullableRef
| FieldTag::NullableArray
| FieldTag::ExternRef
)
}
pub const fn reference(target: *const Schema, nullable: bool) -> Self {
Self {
tag: if nullable {
FieldTag::NullableRef
} else {
FieldTag::Ref
},
target,
}
}
pub const fn array(target: *const Schema, nullable: bool) -> Self {
Self {
tag: if nullable {
FieldTag::NullableArray
} else {
FieldTag::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,
pub fields: *const FieldKind,
}
impl Schema {
pub const fn new(fields: &'static [FieldKind]) -> Self {
assert!(fields.len() <= u32::MAX as usize);
Self {
magic: SCHEMA_MAGIC,
kind: SchemaKind::Struct,
field_count: fields.len() as u32,
fields: fields.as_ptr(),
}
}
pub const fn array(element: &'static [FieldKind]) -> Self {
assert!(element.len() == 1);
Self {
magic: SCHEMA_MAGIC,
kind: SchemaKind::Array,
field_count: 1,
fields: element.as_ptr(),
}
}
}
#[repr(C)]
pub struct Operation {
pub magic: [u8; MAGIC_LEN],
pub function: *const (),
pub schema: *const Schema,
pub kind: OperationKind,
pub field: u32,
}
impl Operation {
pub const fn new(
schema: *const Schema,
kind: OperationKind,
field: u32,
function: *const (),
) -> Self {
Self {
magic: OPERATION_MAGIC,
function,
schema,
kind,
field,
}
}
}
#[repr(C)]
pub struct Export {
pub magic: [u8; MAGIC_LEN],
pub schema: *const Schema,
pub kind: OperationKind,
pub field: u32,
pub name: *const u8,
pub name_len: u32,
}
impl Export {
pub const fn new(
schema: *const Schema,
kind: OperationKind,
field: u32,
name: &'static str,
) -> Self {
assert!(name.len() <= u32::MAX as usize);
Self {
magic: EXPORT_MAGIC,
schema,
kind,
field,
name: name.as_ptr(),
name_len: name.len() as u32,
}
}
}
#[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 params: *const FieldKind,
pub result: *const FieldKind,
pub module: *const u8,
pub module_len: u32,
pub name: *const u8,
pub name_len: u32,
}
impl Function {
pub const fn new(
kind: FunctionKind,
params: &'static [FieldKind],
result: Option<&'static FieldKind>,
module: &'static str,
name: &'static str,
) -> Self {
assert!(params.len() <= u32::MAX as usize);
assert!(module.len() <= u32::MAX as usize);
assert!(name.len() <= u32::MAX as usize);
Self {
magic: FUNCTION_MAGIC,
kind,
param_count: params.len() as u32,
params: params.as_ptr(),
result: match result {
Some(result) => result,
None => core::ptr::null(),
},
module: module.as_ptr(),
module_len: module.len() as u32,
name: name.as_ptr(),
name_len: name.len() as u32,
}
}
}
unsafe impl Sync for FieldKind {}
unsafe impl Sync for Schema {}
unsafe impl Sync for Operation {}
unsafe impl Sync for Export {}
unsafe impl Sync for Function {}
const _: () = {
use core::mem::{offset_of, size_of};
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::<FieldKind>() == layout.field_kind_size as usize);
assert!(offset_of!(FieldKind, target) == layout.field_kind_target_offset as usize);
assert!(size_of::<Schema>() == layout.schema_size 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!(Schema, fields) == layout.schema_fields_offset 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::<Export>() == layout.export_size as usize);
assert!(offset_of!(Export, magic) == layout.export_magic_offset as usize);
assert!(offset_of!(Export, schema) == layout.export_schema_offset as usize);
assert!(offset_of!(Export, kind) == layout.export_kind_offset as usize);
assert!(offset_of!(Export, field) == layout.export_field_offset as usize);
assert!(offset_of!(Export, name) == layout.export_name_offset as usize);
assert!(offset_of!(Export, name_len) == layout.export_name_len_offset as usize);
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, params) == layout.function_params_offset as usize);
assert!(offset_of!(Function, result) == layout.function_result_offset as usize);
assert!(offset_of!(Function, module) == layout.function_module_offset as usize);
assert!(offset_of!(Function, module_len) == layout.function_module_len_offset as usize);
assert!(offset_of!(Function, name) == layout.function_name_offset as usize);
assert!(offset_of!(Function, name_len) == layout.function_name_len_offset as usize);
};