diff --git a/giterated-abi/Cargo.toml b/giterated-abi/Cargo.toml new file mode 100644 index 0000000..c377b21 --- /dev/null +++ b/giterated-abi/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "giterated-abi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/giterated-abi/README.md b/giterated-abi/README.md new file mode 100644 index 0000000..b7c05aa --- /dev/null +++ b/giterated-abi/README.md @@ -0,0 +1,25 @@ +# Versioning + +This versioning documentation specifically refers to the version of the Giterated ABI. The Giterated ABI can, and should, update independently from the rest of the project, as needed. + +## Pre 1.0.0 (Now!) + +You are able to consume `giterated-abi` in your `Cargo.toml` manifest as follows with the expectation nothing should break as `giterated-abi` updates: +``` +giterated-abi = "0.1" +``` + +It is important to still receive patch updates automatically as we may provide shim functionality and better compatibility with future changes in these updates. There may also be security and performance fixes. Furthermore it allows for Cargo to be more precise when selecting the exact dependency version. + +- Stability is **not** guaranteed. +- Breaking changes **can** be made between "minor" semver versions, they **should** be well documented and announced ahead of time. + +## Post 1.0.0 (Future) + +You are able to consume `giterated-abi` in your `Cargo.toml` manifest as follows with the *guarantee* of compatibility. + +``` +giterated-abi = "1" +``` + +After 1.0.0 is released, minor updates **must not** contain changes that violate a reasonable understanding of semver. The ABI is intended to be stable and only changed in a backwards-compatible manner beyond 1.0.0. \ No newline at end of file diff --git a/giterated-abi/src/future.rs b/giterated-abi/src/future.rs new file mode 100644 index 0000000..e399187 --- /dev/null +++ b/giterated-abi/src/future.rs @@ -0,0 +1,56 @@ +use std::marker::PhantomData; + +use crate::{FfiValue, FfiValueMut}; + +#[repr(C)] +pub struct FfiFuture { + poll_fn: unsafe extern "C" fn(FfiValueMut>, FfiValueMut<()>) -> RuntimeFuturePoll, + wake_fn: unsafe extern "C" fn(FfiValueMut>, FfiValueMut<()>), + + poll_state: FfiValue<()>, + waker_state: Option>, + + _output_marker: PhantomData, +} + +unsafe impl Send for FfiFuture where Output: Send {} +unsafe impl Sync for FfiFuture where Output: Sync {} + +impl FfiFuture { + /// Docs here! + /// # SAFETY + /// TODO :( + pub unsafe fn poll(&mut self) -> RuntimeFuturePoll { + todo!() + } + + /// Docs here! + /// # SAFETY + /// TODO :( + pub unsafe fn from_raw( + _poll_fn: unsafe extern "C" fn( + FfiValueMut>, + FfiValueMut<()>, + ) -> RuntimeFuturePoll, + _poll_state: PS, + ) -> Self { + todo!() + } + + /// Docs here! + /// # SAFETY + /// Very not in progress text :) + pub unsafe fn write_waker( + &mut self, + _wake_fn: unsafe extern "C" fn(FfiValueMut>, FfiValueMut<()>), + _waker_state: WS, + ) { + todo!() + } +} + +#[repr(C)] +pub enum RuntimeFuturePoll { + Ready(FfiValue<()>), + Pending, +} diff --git a/giterated-abi/src/heap.rs b/giterated-abi/src/heap.rs new file mode 100644 index 0000000..0bab735 --- /dev/null +++ b/giterated-abi/src/heap.rs @@ -0,0 +1,9 @@ +use crate::FfiValue; + +pub trait HeapPlacable { + unsafe extern "C" fn free(value: FfiValue, taken: bool); +} + +impl HeapPlacable for T { + unsafe extern "C" fn free(_value: FfiValue, _taken: bool) {} +} diff --git a/giterated-abi/src/lib.rs b/giterated-abi/src/lib.rs new file mode 100644 index 0000000..bddf77c --- /dev/null +++ b/giterated-abi/src/lib.rs @@ -0,0 +1,546 @@ +//! Giterated ABI +//! # ABI +//! +//! ## Value ABI +//! +//! At its core, the Giterated Runtime uses the `extern "C"` ABI. What that means is likely platform specific, and doesn't matter. +//! You are intended to compile the Giterated Runtime and Plugins for your local machine, all with a similar idea of what +//! your "local machine" is. +//! +//! Values are passed using the `FFI` type. There are four categories of value that the `FFI` type enables you to pass: +//! +//! | `FFI` Type Category | Placed Backing? | Owned? | +//! |---------------------|-----------------|--------| +//! | Slice | Heap/Stack | No | +//! | Referenced Slice | Stack | No | +//! | Referenced Value | No | No | +//! | Owned Value | Heap | Yes | +//! +//! For an FFI type to have a "placed backing" is for it to have some data structure beyond the data it represents, placed +//! somewhere in memory. Some types only require stack placement while some offer both stack and heap placement. +//! +//! Stack-placed values can be shared by `PinnedRef` and `PinnedMut`, and thus can only be owned by the caller. +//! +//! Heap-placed values can be shared by `Owned`, `PinnedRef`, and `PinnedMut`. They can be owned by any one consumer, +//! When the handle with ownership is `Drop`'d by the sole consumer, it will free the object using the associated `Drop` callback. +//! +//! ### Safety Intents +//! +//! This API is designed to simplify interaction with FFI values, and provide a static ABI for those values to be passed. It +//! is key to enabling ownership across FFI while ensuring associated dropping and allocation freeing logic is ran. +//! +//! The contract the developer has to follow is made simpler by this system, and it allows for generic code to be written to +//! interact with FFI-given values and pass values using FFI. +//! +//! ### Stability Guarantees +//! +//! There are no plans to guarantee stability until 1.0.0. At that point you can expect the ABI to remain stable until the major version +//! is incremented again. There will be an appropriate deprecation process and changeover period. +//! +//! ### Memory Representation +//! +//! Please check out the source code, sorry if you needed that from the docs! +//! +//! ## Object, Operation, Setting, Value, Plugin, and Runtime ABIs +//! +//! The Giterated Runtime uses vtables to accomplish the goal of ensuring maximum compatibility. For every object that is shared +//! between plugins, a vtable is used to allow each plugin to provide their own code for interacting with the object. +//! +//! When objects switch "runtime domains" (e.g. host -> plugin, plugin -> plugin, plugin -> host), their vtable is swapped out +//! for the new runtime domain's own vtables. +//! +//! ### Untyped "Objects" (see above header for list) +//! +//! Untyped objects, in memory, are represented by a data pointer and a vtable pointer. Exactly like Rust traits. However, to +//! prevent small compilation differences and other random garbage from making the interface not perfectly compatible we use +//! the local plugin's idea of the vtable for the object at all times. An object that the plugin does not have a vtable for cannot +//! be relevant to the plugin. +//! +//! It is important that the object's base representation in memory remain unchanged between major versions, but the vtables that provide methods for +//! that object may be grown. The methods that operate on that object may be changed in an non-breaking fashion, and bugs can be +//! fixed. +//! +//! ## Futures ABI +//! +//! The Giterated Runtime has an async runtime that allows for futures to be shared and awaited across FFI boundaries while only +//! executing the future within the context of the Plugin who is running the underlying future. +//! +//! Futures are spawned onto the `RuntimeState` with the `RuntimeFuturesExt` trait. This takes a Rust future, boxes it, and +//! provides a `RuntimeFuture` handle that can be used to drive the underlying Rust future locally. The `RuntimeFuture` handle +//! is thread-safe and can be shared with the callee and `.await`'d directly like any other future. +//! +//! ### RuntimeFuture +//! +//! The `RuntimeFuture` mixes a vtable with data to allow for any caller to drive a spawned future. It contains: +//! +//! - A `poll_fn` which is used to poll the future for `Ready`-ness +//! - A `wake_fn` which is used to wake the callee to poll for (expected) `Ready`-ness, it is populated when the `RuntimeFuture` is `await`'d. +//! +//! When the `RuntimeFuture` is polled, it causes the inner future to also be polled. We provide the inner future with a waker +//! that triggers the `RuntimeFuture`'s waker so it is polled again. Breaking character to point out how freaking cool that is. +//! +//! `RuntimeFuture`s drop the associated inner future as they drop. + +mod future; +mod heap; +pub mod result; +pub mod vtable; +use abi_backing::{HeapValueBacking, SliceBacking}; +pub use future::{FfiFuture, RuntimeFuturePoll}; +use heap::HeapPlacable; + +use std::{ + marker::PhantomData, + mem::{transmute, MaybeUninit}, + ops::{Deref, DerefMut}, +}; + +use abi_types::{Slice, SliceMut, SliceRef, Value, ValueMut, ValueRef}; +use guards::{HeapPinnedSlice, HeapPinnedValue, StackPinnedSlice, StackPinnedValue}; + +#[doc(hidden)] +pub mod prelude { + pub use crate::Ffi; + pub use crate::StackPinned; + pub use crate::{FfiSlice, FfiSliceRef, FfiValue, FfiValueRef}; +} + +/// Slice Reference +/// Heap or Stack Placed +pub type FfiSliceRef = Ffi; + +/// Mutable Slice Reference +/// Heap or Stack Placed +pub type FfiSliceMut = Ffi; + +/// Value Reference +/// Heap or Stack Placed +pub type FfiValueRef = Ffi; + +/// Mutable Value Reference +/// Heap or Stack Placed +pub type FfiValueMut = Ffi; + +/// Owned Value +/// Heap Placed +pub type FfiValue = Ffi; + +/// Owned Slice +/// Heap Placed +pub type FfiSlice = Ffi; + +pub mod value_ex { + use crate::{abi_types::Value, Ffi}; + + pub type FfiValueUntyped = Ffi<(), Value>; + pub type FfiValueRefUntyped = Ffi<(), Value>; +} + +/// A value passed over FFI, following the Giterated ABI. +/// +/// The function of the [`Ffi`] type is to take an arbitrary pointer and send it over FFI. +/// Both the caller and callee **must** have the same understanding of what the pointer represents. +/// The [`Ffi`] type is also used to encode ownership information. +/// +/// # The Pointer +/// The pointer contained within the [`Ffi`] is transmuted based on the provided `ABI` on the +/// [`Ffi`] type signature. +#[repr(transparent)] +pub struct Ffi { + inner: *const (), + _type_marker: PhantomData, + _abi_marker: PhantomData, +} + +impl FfiSlice { + #[inline(always)] + pub fn pin(&self) -> HeapPinnedSlice<'_, T> { + unsafe { HeapPinnedSlice::from_raw(self) } + } +} + +impl Deref for FfiSlice { + type Target = [T]; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + let inner: *const SliceBacking<[T]> = unsafe { transmute(self.inner) }; + let backing = unsafe { inner.as_ref().unwrap_unchecked() }; + + unsafe { + core::slice::from_raw_parts( + backing.slice as *mut T, + usize::try_from(backing.count).unwrap_unchecked(), + ) + } + } +} +impl DerefMut for FfiSlice { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + let inner: *mut SliceBacking<[T]> = unsafe { transmute(self.inner) }; + let backing = unsafe { inner.as_mut().unwrap_unchecked() }; + + unsafe { + core::slice::from_raw_parts_mut( + backing.slice as *mut T, + usize::try_from(backing.count).unwrap_unchecked(), + ) + } + } +} + +impl FfiSliceRef {} + +impl Deref for FfiSliceRef<[T]> { + type Target = [T]; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + let inner: *const SliceBacking<[T]> = unsafe { transmute(self.inner) }; + + let backing = unsafe { inner.as_ref().unwrap_unchecked() }; + + unsafe { + core::slice::from_raw_parts( + backing.slice as *const T, + usize::try_from(backing.count).unwrap_unchecked(), + ) + } + } +} + +impl FfiValueRef {} + +impl Deref for FfiValueRef { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + let inner: *const T = unsafe { transmute(self.inner) }; + + match unsafe { inner.as_ref() } { + Some(val) => val, + _ => unreachable!(), + } + } +} + +impl Deref for FfiValueMut { + type Target = T; + + fn deref(&self) -> &Self::Target { + let inner: *mut T = unsafe { transmute(self.inner) }; + + unsafe { inner.as_ref().unwrap_unchecked() } + } +} +impl DerefMut for FfiValueMut { + fn deref_mut(&mut self) -> &mut Self::Target { + let inner: *mut T = unsafe { transmute(self.inner) }; + + unsafe { inner.as_mut().unwrap_unchecked() } + } +} + +impl std::fmt::Display for FfiValueRef +where + T: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unsafe { (self.inner as *const T).as_ref().unwrap() }.fmt(f) + } +} + +impl FfiValue { + pub fn new(value: T) -> Self { + let value = Box::new(HeapValueBacking { + value, + drop_fn: ::free, + }); + + FfiValue { + inner: Box::into_raw(value) as _, + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + + pub fn pin(&self) -> HeapPinnedValue<'_, T> { + unsafe { HeapPinnedValue::from_raw(self) } + } + + pub fn take(self) -> T { + // This all boils down to moving `T` out of the `FfiValue` and dropping the backing + // storage for said `FfiValue`. Despite the use of unsafe this is exactly how moving + // a value onto the stack works. + + let inner = self.inner as *mut T; + let mut move_target: MaybeUninit = MaybeUninit::zeroed(); + + unsafe { move_target.as_mut_ptr().copy_from(inner, 1) } + + let inner_descriptor: *mut HeapValueBacking = unsafe { transmute(self.inner) }; + + unsafe { (inner_descriptor.as_mut().unwrap_unchecked().drop_fn)(self, true) }; + + unsafe { move_target.assume_init() } + } +} + +impl Deref for FfiValue { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + let inner: *const T = unsafe { transmute(self.inner) }; + + unsafe { inner.as_ref().unwrap_unchecked() } + } +} +impl DerefMut for FfiValue { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + let inner: *mut T = unsafe { transmute(self.inner) }; + + unsafe { inner.as_mut().unwrap_unchecked() } + } +} + +mod abi_backing { + use std::{marker::PhantomData, mem::transmute}; + + use crate::{FfiSlice, FfiValue}; + + #[repr(C)] + pub struct HeapValueBacking { + pub(super) value: T, + pub(super) drop_fn: unsafe extern "C" fn(value: FfiValue, taken: bool), + } + + pub struct SliceBacking { + pub(crate) count: u64, + pub(crate) slice: *const (), + _marker: PhantomData, + } + + impl SliceBacking { + /// Creates a new slice backing from a raw slice pointer and a count. + /// + /// # SAFETY + /// + /// `slice` **must** refer to a valid slice, with a length greater than or equal to the + /// value provided as `count`. + #[inline(always)] + pub(crate) unsafe fn from_raw(count: u64, slice: *const ()) -> Self { + Self { + count, + slice, + _marker: PhantomData, + } + } + + /// Creates a new slice backing from an [`FfiSlice`]. + /// + /// # SAFETY + /// + /// The resultant [`SliceBacking`] **must not** outlive the backing [`FfiSlice`]. + #[inline(always)] + pub(crate) unsafe fn from_heap(slice: &FfiSlice) -> Self { + let heap_backing: *const SliceBacking = unsafe { transmute(slice.inner) }; + + let heap_backing = unsafe { heap_backing.as_ref().unwrap_unchecked() }; + + Self { + count: heap_backing.count, + slice: heap_backing.slice, + _marker: PhantomData, + } + } + } +} + +mod guards { + use std::marker::PhantomData; + + use crate::{ + abi_backing::SliceBacking, Ffi, FfiSlice, FfiSliceMut, FfiSliceRef, FfiValue, FfiValueMut, + FfiValueRef, + }; + + #[repr(transparent)] + pub struct StackPinnedSlice<'v, T: ?Sized> { + _lifetime: PhantomData<&'v T>, + slice: SliceBacking, + } + + impl<'v, T> StackPinnedSlice<'v, T> { + #[inline(always)] + pub fn as_ref(&self) -> FfiSliceRef { + FfiSliceRef { + inner: &self.slice as *const _ as *const (), + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + + #[inline(always)] + pub fn as_mut(&mut self) -> FfiSliceMut { + FfiSliceMut { + inner: &mut self.slice as *mut _ as *mut (), + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + } + + impl<'v, T> StackPinnedSlice<'v, T> { + /// Creates a stack pinned slice guard from a borrowed slice. + /// + /// # SAFETY + /// This function itself isn't "unsafe" but other code will become unsafe if the `slice` + /// becomes invalid or moves. You'd have to violate safety rules somewhere else to do that, + /// though. + #[inline(always)] + pub(crate) unsafe fn from_raw(slice: &'v [T]) -> StackPinnedSlice<'v, T> { + Self { + _lifetime: PhantomData, + slice: SliceBacking::from_raw( + u64::try_from(slice.len()).unwrap(), + slice.as_ptr() as *const (), + ), + } + } + } + + pub struct StackPinnedValue<'v, T> { + value_ref: &'v T, + } + + impl<'v, T> StackPinnedValue<'v, T> { + /// Grants a reference to the pinned value. + /// + /// # SAFETY + /// - The granted reference **must not** outlive the lifetime of `&self`. + /// - There **must not** be a mutable reference created or mutable dereference performed during the lifetime of the [`FfiValueRef`]. + #[inline(always)] + pub unsafe fn grant_ref(&self) -> FfiValueRef { + Ffi { + inner: self.value_ref as *const _ as *const (), + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + } + + impl<'v, T> StackPinnedValue<'v, T> { + #[inline(always)] + pub(crate) fn from_raw(value: &'v T) -> Self { + Self { value_ref: value } + } + } + + pub struct HeapPinnedSlice<'v, T> { + _lifetime: PhantomData<&'v T>, + slice: SliceBacking, + } + + impl<'v, T> HeapPinnedSlice<'v, T> { + /// Creates a pin guard from a heap placed slice. + /// + /// # SAFETY + /// The `slice` **must not** be moved and **must not** have a mutable reference given during the lifetime + /// of the returned [`HeapPinnedSlice`] guard. + #[inline(always)] + pub(crate) unsafe fn from_raw(slice: &'v FfiSlice) -> HeapPinnedSlice<'v, T> { + Self { + _lifetime: PhantomData, + slice: SliceBacking::from_heap(slice), + } + } + + pub unsafe fn grant_ref(&self) -> FfiSliceRef { + FfiSliceRef { + inner: &self.slice as *const _ as *const (), + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + + pub unsafe fn grant_mut(&mut self) -> FfiSliceMut { + FfiSliceMut { + inner: &mut self.slice as *mut _ as *mut (), + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + } + + #[repr(transparent)] + pub struct HeapPinnedValue<'v, T> { + value: &'v FfiValue, + } + + impl<'v, T> HeapPinnedValue<'v, T> { + #[inline(always)] + pub(crate) unsafe fn from_raw(value: &'v FfiValue) -> HeapPinnedValue<'v, T> { + Self { value } + } + + #[inline(always)] + pub unsafe fn grant_ref(&self) -> FfiValueRef { + FfiValueRef { + inner: self.value.inner, + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + + #[inline(always)] + pub unsafe fn grant_mut(&mut self) -> FfiValueMut { + FfiValueMut { + inner: self.value.inner, + _type_marker: PhantomData, + _abi_marker: PhantomData, + } + } + } +} + +mod abi_types { + pub struct Slice; + + pub struct SliceRef; + + pub struct SliceMut; + + pub struct ValueRef; + + pub struct ValueMut; + + pub struct Value; +} + +pub trait StackPinned<'p> { + type Pinned: ?Sized + 'p; + + fn pin(&'p self) -> Self::Pinned; +} + +impl<'p, T: 'p> StackPinned<'p> for [T] { + type Pinned = StackPinnedSlice<'p, T>; + + #[inline(always)] + fn pin(&'p self) -> StackPinnedSlice<'p, T> { + unsafe { StackPinnedSlice::from_raw(self) } + } +} + +impl<'p, T: 'p> StackPinned<'p> for T { + type Pinned = StackPinnedValue<'p, T>; + + #[inline(always)] + fn pin(&'p self) -> Self::Pinned { + StackPinnedValue::from_raw(self) + } +} diff --git a/giterated-abi/src/result.rs b/giterated-abi/src/result.rs new file mode 100644 index 0000000..a5eb8bb --- /dev/null +++ b/giterated-abi/src/result.rs @@ -0,0 +1,12 @@ +use crate::FfiSlice; + +#[repr(C)] +pub enum Result { + Ok(T), + Err(E), +} + +#[repr(C)] +pub struct Error { + last_error: FfiSlice, +} diff --git a/giterated-abi/src/vtable/mod.rs b/giterated-abi/src/vtable/mod.rs new file mode 100644 index 0000000..b17a1e2 --- /dev/null +++ b/giterated-abi/src/vtable/mod.rs @@ -0,0 +1,40 @@ +use std::{ + marker::PhantomData, mem::transmute, ops::Deref +}; + +mod object; +pub mod operation; +mod setting; +mod value; + +pub use object::*; +pub use setting::*; +pub use value::*; + +pub trait ObjectABI { + type VTable; +} + +#[repr(transparent)] +pub struct VTable { + _marker: PhantomData, +} + +impl VTable { + /// Creates a new `VTable` reference to a static vtable in memory. + /// + /// Might be unsafe? It seems like it should be safe... + pub const fn new(vtable: &'static V) -> &'static Self { + // We're going to transmute the reference to the typed vtable to us + // which will probably be fine + unsafe { transmute(vtable) } + } +} + +impl Deref for VTable { + type Target = T::VTable; + + fn deref(&self) -> &Self::Target { + unsafe { transmute(self) } + } +} diff --git a/giterated-abi/src/vtable/object.rs b/giterated-abi/src/vtable/object.rs new file mode 100644 index 0000000..06ca5fe --- /dev/null +++ b/giterated-abi/src/vtable/object.rs @@ -0,0 +1,38 @@ +use std::ffi::CStr; + +use crate::{ + result::{Error, Result}, + FfiSlice, FfiSliceRef, FfiValueRef, +}; + +use super::{ObjectABI, VTable}; + +#[repr(C)] +pub struct Object { + inner: (), + vtable: &'static VTable, +} + +impl ObjectABI for Object { + type VTable = ObjectVTable; +} + +pub struct ObjectVTable { + pub object_kind: &'static CStr, + pub to_str: unsafe extern "C" fn(this: FfiValueRef) -> FfiSlice, + pub from_str: unsafe extern "C" fn(from: FfiSliceRef) -> Result, + pub home_uri: unsafe extern "C" fn(this: FfiValueRef) -> FfiSlice, +} + +impl ObjectVTable { + pub fn new() -> Self { + todo!() + } +} + +pub trait IntoObjectVTable { + fn object_kind() -> &'static CStr; + unsafe extern "C" fn to_str(this: FfiValueRef) -> FfiSlice; + unsafe extern "C" fn from_str(from: FfiSliceRef) -> Result; + unsafe extern "C" fn home_uri(this: FfiValueRef) -> FfiSlice; +} diff --git a/giterated-abi/src/vtable/operation.rs b/giterated-abi/src/vtable/operation.rs new file mode 100644 index 0000000..d41d082 --- /dev/null +++ b/giterated-abi/src/vtable/operation.rs @@ -0,0 +1,63 @@ +use std::ffi::CStr; + +use crate::{ + result::{Error, Result}, + value_ex::{FfiValueRefUntyped, FfiValueUntyped}, + FfiSlice, FfiSliceRef, FfiValue, FfiValueRef, +}; + +use super::{ObjectABI, VTable}; + +#[repr(C)] +pub struct Operation { + inner: FfiValue<()>, + vtable: &'static VTable, +} + +impl ObjectABI for Operation { + type VTable = OperationVTable; +} + +pub struct OperationVTable { + pub operation_kind: &'static CStr, + pub operation_serialize: + unsafe extern "C" fn(this: FfiValueRef) -> Result, Error>, + pub operation_deserialize: + unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> Result, + pub success_serialize: + unsafe extern "C" fn(success: FfiValueRefUntyped) -> Result, Error>, + pub success_deserialize: + unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> Result, + pub failure_serialize: + unsafe extern "C" fn(failure: FfiValueRefUntyped) -> Result, Error>, + pub failure_deserialize: + unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> Result, +} + +impl OperationVTable { + pub const fn new() -> Self { + todo!() + } +} + +pub trait IntoOperationVTable { + fn operation_kind() -> &'static CStr; + unsafe extern "C" fn operation_serialize( + this: FfiValueRef, + ) -> Result, Error>; + unsafe extern "C" fn operation_deserialize( + buffer: FfiSliceRef<[u8]>, + ) -> Result; + unsafe extern "C" fn success_serialize( + success: FfiValueRefUntyped, + ) -> Result, Error>; + unsafe extern "C" fn success_deserialize( + buffer: FfiSliceRef<[u8]>, + ) -> Result; + unsafe extern "C" fn failure_serialize( + failure: FfiValueRefUntyped, + ) -> Result, Error>; + unsafe extern "C" fn failure_deserialize( + buffer: FfiSliceRef<[u8]>, + ) -> Result; +} diff --git a/giterated-abi/src/vtable/setting.rs b/giterated-abi/src/vtable/setting.rs new file mode 100644 index 0000000..25d5414 --- /dev/null +++ b/giterated-abi/src/vtable/setting.rs @@ -0,0 +1,32 @@ +use crate::{ + result::{Error, Result}, + FfiSlice, FfiSliceRef, +}; + +use super::{ObjectABI, VTable}; + +#[repr(C)] +pub struct Setting { + inner: (), + vtable: &'static VTable, +} + +impl ObjectABI for Setting { + type VTable = SettingVTable; +} + +pub struct SettingVTable { + pub serialize: unsafe extern "C" fn(this: Setting) -> Result, Error>, + pub deserialize: unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> Result, +} + +impl SettingVTable { + pub fn new() -> Self { + todo!() + } +} + +pub trait IntoSettingVTable { + unsafe extern "C" fn serialize(this: Setting) -> Result, Error>; + unsafe extern "C" fn deserialize(buffer: FfiSliceRef<[u8]>) -> Result; +} diff --git a/giterated-abi/src/vtable/value.rs b/giterated-abi/src/vtable/value.rs new file mode 100644 index 0000000..56f2b06 --- /dev/null +++ b/giterated-abi/src/vtable/value.rs @@ -0,0 +1,35 @@ +use crate::{ + result::{Error, Result}, + FfiSlice, FfiSliceRef, +}; + +use super::{ObjectABI, VTable}; + +#[repr(C)] +pub struct Value { + inner: (), + vtable: &'static VTable, +} + +impl ObjectABI for Value { + type VTable = ValueVTable; +} + +pub struct ValueVTable { + pub serialize: unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> Result, + pub deserialize: unsafe extern "C" fn(this: Value) -> Result, Error>, +} + +impl ValueVTable { + pub const fn new() -> Self { + Self { + serialize: V::serialize, + deserialize: V::deserialize, + } + } +} + +pub trait IntoValueVTable { + unsafe extern "C" fn serialize(buffer: FfiSliceRef<[u8]>) -> Result; + unsafe extern "C" fn deserialize(this: Value) -> Result, Error>; +}