pub type DefaultThunk = unsafe extern "C" fn(ctx: *const (), slot: *mut u8);
#[derive(Clone, Debug)]
pub struct DefaultOp {
pub offset: usize,
pub ctx: *const (),
pub default: DefaultThunk,
}
#[derive(Clone, Copy, Debug)]
pub struct SeqThunks {
pub ctx: *const (),
pub from_raw_parts:
unsafe extern "C" fn(ctx: *const (), list: *mut u8, ptr: *mut u8, len: usize, cap: usize),
pub len: unsafe extern "C" fn(ctx: *const (), list: *const u8) -> usize,
pub data: unsafe extern "C" fn(ctx: *const (), list: *const u8) -> *const u8,
}
#[derive(Clone, Copy, Debug)]
pub struct SetThunks {
pub ctx: *const (),
pub len: unsafe extern "C" fn(ctx: *const (), set: *const u8) -> usize,
pub init_with_capacity: unsafe extern "C" fn(ctx: *const (), set: *mut u8, cap: usize),
pub insert: unsafe extern "C" fn(ctx: *const (), set: *mut u8, value: *mut u8) -> bool,
pub iter_init: unsafe extern "C" fn(ctx: *const (), set: *const u8) -> *mut (),
pub iter_next:
unsafe extern "C" fn(ctx: *const (), iter: *mut (), value_out: *mut *const u8) -> bool,
pub iter_dealloc: unsafe extern "C" fn(ctx: *const (), iter: *mut ()),
}
pub type ByteValidator = unsafe extern "C" fn(ptr: *const u8, len: usize) -> bool;
#[derive(Clone, Copy, Debug)]
pub struct BorrowThunks {
pub ctx: *const (),
pub set_borrowed:
unsafe extern "C" fn(ctx: *const (), field: *mut u8, ptr: *const u8, len: usize) -> bool,
pub len: unsafe extern "C" fn(ctx: *const (), field: *const u8) -> usize,
pub data: unsafe extern "C" fn(ctx: *const (), field: *const u8) -> *const u8,
}
#[derive(Clone, Copy, Debug)]
pub struct OptionThunks {
pub ctx: *const (),
pub is_some: unsafe extern "C" fn(ctx: *const (), option: *const u8) -> bool,
pub get_value: unsafe extern "C" fn(ctx: *const (), option: *const u8) -> *const u8,
pub init_some: unsafe extern "C" fn(ctx: *const (), option: *mut u8, value: *mut u8),
pub init_none: unsafe extern "C" fn(ctx: *const (), option: *mut u8),
}
#[derive(Clone, Copy, Debug)]
pub struct MapThunks {
pub ctx: *const (),
pub len: unsafe extern "C" fn(ctx: *const (), map: *const u8) -> usize,
pub init_with_capacity: unsafe extern "C" fn(ctx: *const (), map: *mut u8, cap: usize),
pub insert: unsafe extern "C" fn(ctx: *const (), map: *mut u8, key: *mut u8, value: *mut u8),
pub iter_init: unsafe extern "C" fn(ctx: *const (), map: *const u8) -> *mut (),
pub iter_next: unsafe extern "C" fn(
ctx: *const (),
iter: *mut (),
key_out: *mut *const u8,
value_out: *mut *const u8,
) -> bool,
pub iter_dealloc: unsafe extern "C" fn(ctx: *const (), iter: *mut ()),
}
#[derive(Clone, Copy, Debug)]
pub struct ResultThunks {
pub ctx: *const (),
pub is_ok: unsafe extern "C" fn(ctx: *const (), result: *const u8) -> bool,
pub get_ok: unsafe extern "C" fn(ctx: *const (), result: *const u8) -> *const u8,
pub get_err: unsafe extern "C" fn(ctx: *const (), result: *const u8) -> *const u8,
pub init_ok: unsafe extern "C" fn(ctx: *const (), result: *mut u8, value: *mut u8),
pub init_err: unsafe extern "C" fn(ctx: *const (), result: *mut u8, value: *mut u8),
}
#[derive(Clone, Copy, Debug)]
pub struct PointerThunks {
pub ctx: *const (),
pub borrow: unsafe extern "C" fn(ctx: *const (), pointer: *const u8) -> *const u8,
pub init: unsafe extern "C" fn(ctx: *const (), pointer: *mut u8, value: *mut u8),
}
#[derive(Clone, Copy, Debug)]
pub struct OpaqueThunks {
pub ctx: *const (),
pub encode: unsafe extern "C" fn(ctx: *const (), field: *const u8, out: *mut Vec<u8>),
pub decode:
unsafe extern "C" fn(ctx: *const (), bytes: *const u8, len: usize, slot: *mut u8) -> bool,
}
#[derive(Clone, Debug)]
pub struct Descriptor<SchemaRef> {
pub schema: SchemaRef,
pub layout: Layout,
pub access: Access<SchemaRef>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Layout {
pub size: usize,
pub align: usize,
}
#[derive(Clone, Debug)]
pub enum Access<SchemaRef> {
Scalar,
Record(RecordAccess<SchemaRef>),
Enum(EnumAccess<SchemaRef>),
Option(OptionAccess<SchemaRef>),
Array {
element: Box<Descriptor<SchemaRef>>,
count: usize,
stride: usize,
},
Tensor(TensorAccess<SchemaRef>),
Sequence(SequenceAccess<SchemaRef>),
Set(SetAccess<SchemaRef>),
Map(MapAccess<SchemaRef>),
Result(ResultAccess<SchemaRef>),
Pointer(PointerAccess<SchemaRef>),
Dynamic,
Opaque(OpaqueThunks),
Recurse,
}
#[derive(Clone, Debug)]
pub struct RecordAccess<SchemaRef> {
pub fields: Vec<FieldAccess<SchemaRef>>,
pub construct: Construct,
}
#[derive(Clone, Debug)]
pub struct FieldAccess<SchemaRef> {
pub offset: usize,
pub descriptor: Descriptor<SchemaRef>,
pub default: Option<FieldDefault>,
}
#[derive(Clone, Copy, Debug)]
pub struct FieldDefault {
pub ctx: *const (),
pub thunk: DefaultThunk,
}
#[derive(Clone, Debug)]
pub enum Construct {
InPlace,
Thunk(Thunk),
}
#[derive(Clone, Debug)]
pub struct EnumAccess<SchemaRef> {
pub tag: Tag,
pub variants: Vec<VariantAccess<SchemaRef>>,
}
#[derive(Clone, Debug)]
pub enum Tag {
Direct { offset: usize, width: usize },
Niche { offset: usize, width: usize },
Thunk { read: Thunk, write: Thunk },
}
#[derive(Clone, Debug)]
pub struct VariantAccess<SchemaRef> {
pub index: u32,
pub selector: u64,
pub payload: RecordAccess<SchemaRef>,
}
#[derive(Clone, Debug)]
pub struct OptionAccess<SchemaRef> {
pub presence: Presence,
pub some: Box<Descriptor<SchemaRef>>,
}
#[derive(Clone, Debug)]
pub enum Presence {
Tag {
offset: usize,
width: usize,
none_value: u64,
},
Niche {
offset: usize,
width: usize,
none_pattern: Vec<u8>,
},
Thunk {
is_some: Thunk,
set_none: Thunk,
set_some: Thunk,
},
Vtable(OptionThunks),
}
#[derive(Clone, Debug)]
pub struct SequenceAccess<SchemaRef> {
pub element: Box<Descriptor<SchemaRef>>,
pub storage: SequenceStorage,
}
#[derive(Clone, Debug)]
pub struct SetAccess<SchemaRef> {
pub element: Box<Descriptor<SchemaRef>>,
pub storage: SetStorage,
}
#[derive(Clone, Debug)]
pub enum SetStorage {
Vtable(SetThunks),
}
#[derive(Clone, Debug)]
pub enum SequenceStorage {
Owned {
ptr_offset: usize,
len_offset: usize,
cap_offset: Option<usize>,
allocate: Thunk,
},
Borrowed {
ptr_offset: usize,
len_offset: usize,
},
Thunk { len: Thunk, get: Thunk, push: Thunk },
Vtable(SeqThunks),
BorrowedVtable(BorrowThunks),
}
#[derive(Clone, Debug)]
pub struct ResultAccess<SchemaRef> {
pub ok: Box<Descriptor<SchemaRef>>,
pub err: Box<Descriptor<SchemaRef>>,
pub thunks: ResultThunks,
}
#[derive(Clone, Debug)]
pub struct PointerAccess<SchemaRef> {
pub pointee: Box<Descriptor<SchemaRef>>,
pub thunks: PointerThunks,
}
#[derive(Clone, Debug)]
pub struct MapAccess<SchemaRef> {
pub key: Box<Descriptor<SchemaRef>>,
pub value: Box<Descriptor<SchemaRef>>,
pub storage: MapStorage,
}
#[derive(Clone, Debug)]
pub enum MapStorage {
Thunk {
len: Thunk,
iterate: Thunk,
insert: Thunk,
},
Vtable(MapThunks),
}
#[derive(Clone, Debug)]
pub struct TensorAccess<SchemaRef> {
pub element: Box<Descriptor<SchemaRef>>,
pub shape: Thunk,
pub data: SequenceStorage,
pub reshape: Thunk,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Thunk {
pub name: String,
}
pub type MemProgram<BlockId> = crate::Program<MemOp<BlockId>>;
#[derive(Clone, Debug)]
pub enum MemOp<BlockId> {
Scalar {
offset: usize,
size: usize,
align: usize,
},
NativeInt {
offset: usize,
mem_size: usize,
signed: bool,
},
Sequence(Box<SeqOp<BlockId>>),
Set(Box<SetOp<BlockId>>),
Bytes(Box<BytesOp>),
Borrow(Box<BorrowOp>),
Option(Box<OptionOp<BlockId>>),
Enum(Box<EnumOp<BlockId>>),
Map(Box<MapOp<BlockId>>),
Dynamic { field_offset: usize },
Result(Box<ResultOp<BlockId>>),
Pointer(Box<PointerOp<BlockId>>),
SkipWire(Box<SkipOp>),
Default(Box<DefaultOp>),
Opaque(Box<OpaqueOp>),
CallBlock { schema: BlockId, offset: usize },
}
#[derive(Clone, Debug)]
pub enum SkipOp {
Scalar { size: usize, align: usize },
Bytes { stride: usize, elem_align: usize },
Seq(Box<SkipOp>),
Option(Box<SkipOp>),
Enum(Vec<(u32, Vec<SkipOp>)>),
Map(Box<SkipOp>, Box<SkipOp>),
Struct(Vec<SkipOp>),
Dynamic,
}
#[derive(Clone, Debug)]
pub struct SeqOp<BlockId> {
pub field_offset: usize,
pub element: MemProgram<BlockId>,
pub stride: usize,
pub elem_align: usize,
pub min_wire: usize,
pub thunks: SeqThunks,
}
#[derive(Clone, Debug)]
pub struct SetOp<BlockId> {
pub field_offset: usize,
pub element: MemProgram<BlockId>,
pub elem_size: usize,
pub elem_align: usize,
pub min_wire: usize,
pub thunks: SetThunks,
}
#[derive(Clone, Debug)]
pub struct BytesOp {
pub field_offset: usize,
pub stride: usize,
pub elem_align: usize,
pub validate: ByteValidator,
pub thunks: SeqThunks,
}
#[derive(Clone, Debug)]
pub struct BorrowOp {
pub field_offset: usize,
pub stride: usize,
pub elem_align: usize,
pub thunks: BorrowThunks,
}
#[derive(Clone, Debug)]
pub struct OptionOp<BlockId> {
pub field_offset: usize,
pub some: MemProgram<BlockId>,
pub inner_size: usize,
pub inner_align: usize,
pub thunks: OptionThunks,
}
#[derive(Clone, Debug)]
pub struct EnumOp<BlockId> {
pub tag_offset: usize,
pub tag_width: usize,
pub variants: Vec<EnumVariantOp<BlockId>>,
pub writer_only: Vec<u32>,
}
#[derive(Clone, Debug)]
pub struct EnumVariantOp<BlockId> {
pub wire_index: u32,
pub selector: u64,
pub payload: MemProgram<BlockId>,
}
#[derive(Clone, Debug)]
pub struct MapOp<BlockId> {
pub field_offset: usize,
pub key: MemProgram<BlockId>,
pub value: MemProgram<BlockId>,
pub key_size: usize,
pub key_align: usize,
pub value_size: usize,
pub value_align: usize,
pub thunks: MapThunks,
}
#[derive(Clone, Debug)]
pub struct ResultOp<BlockId> {
pub field_offset: usize,
pub ok: MemProgram<BlockId>,
pub ok_size: usize,
pub ok_align: usize,
pub ok_wire_index: u32,
pub err: MemProgram<BlockId>,
pub err_size: usize,
pub err_align: usize,
pub err_wire_index: u32,
pub thunks: ResultThunks,
}
#[derive(Clone, Debug)]
pub struct PointerOp<BlockId> {
pub field_offset: usize,
pub pointee: MemProgram<BlockId>,
pub pointee_size: usize,
pub pointee_align: usize,
pub thunks: PointerThunks,
}
#[derive(Clone, Debug)]
pub struct OpaqueOp {
pub field_offset: usize,
pub thunks: OpaqueThunks,
}
#[must_use]
pub fn fuse<BlockId>(program: MemProgram<BlockId>) -> MemProgram<BlockId> {
let mut out: MemProgram<BlockId> = Vec::with_capacity(program.len());
let mut wire_pos: Option<usize> = Some(0);
for op in program {
match op {
MemOp::Scalar {
offset,
size,
align,
} => {
let pad = wire_pos.map(|p| align.wrapping_sub(p & (align - 1)) & (align - 1));
let fuses = pad == Some(0)
&& matches!(
out.last(),
Some(MemOp::Scalar { offset: po, size: ps, .. }) if po + ps == offset
);
if fuses {
if let Some(MemOp::Scalar { size: ps, .. }) = out.last_mut() {
*ps += size;
}
} else {
out.push(MemOp::Scalar {
offset,
size,
align,
});
}
wire_pos = wire_pos.map(|p| p + pad.unwrap_or(0) + size);
}
MemOp::NativeInt {
offset,
mem_size,
signed,
} => {
let align = 8usize;
let size = 8usize;
let pad = wire_pos.map(|p| align.wrapping_sub(p & (align - 1)) & (align - 1));
out.push(MemOp::NativeInt {
offset,
mem_size,
signed,
});
wire_pos = wire_pos.map(|p| p + pad.unwrap_or(0) + size);
}
seq @ (MemOp::Sequence(_)
| MemOp::Set(_)
| MemOp::Bytes(_)
| MemOp::Borrow(_)
| MemOp::Option(_)
| MemOp::Enum(_)
| MemOp::Map(_)
| MemOp::Result(_)
| MemOp::Pointer(_)
| MemOp::Dynamic { .. }
| MemOp::Opaque(_)
| MemOp::CallBlock { .. }
| MemOp::SkipWire(_)) => {
out.push(seq);
wire_pos = None;
}
def @ MemOp::Default(_) => out.push(def),
}
}
out
}