use {
bytes::BufMut,
sealed::sealed,
serde::{Deserialize, Serialize},
smallvec::SmallVec,
std::any::TypeId,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Type {
Primitive(Primitive),
String,
Array(Box<Array>),
Object(Box<Object>),
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum Primitive {
Void,
Bool,
Int32,
Int64,
Float32,
Float64,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TypeRef<'a> {
Primitive(Primitive),
String,
Array(&'a Array),
Object(&'a Object),
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct Object {
class: String,
fields: SmallVec<[Field; 2]>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Field {
name: String,
ty: Type,
offset: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Array {
elem_ty: Type,
len: usize,
}
impl Type {
pub fn size(&self) -> usize {
self.as_ref().size()
}
pub fn as_ref(&self) -> TypeRef<'_> {
match self {
Type::Primitive(primitive) => TypeRef::Primitive(*primitive),
Type::String => TypeRef::String,
Type::Array(array) => TypeRef::Array(array.as_ref()),
Type::Object(object) => TypeRef::Object(object.as_ref()),
}
}
pub fn as_object(&self) -> Option<&Object> {
match self {
Type::Object(object) => Some(object),
_ => None,
}
}
pub(crate) fn type_id(&self) -> Option<TypeId> {
match self {
Type::Primitive(Primitive::Void) => Some(TypeId::of::<()>()),
Type::Primitive(Primitive::Bool) => Some(TypeId::of::<bool>()),
Type::Primitive(Primitive::Int32) => Some(TypeId::of::<i32>()),
Type::Primitive(Primitive::Int64) => Some(TypeId::of::<i64>()),
Type::Primitive(Primitive::Float32) => Some(TypeId::of::<f32>()),
Type::Primitive(Primitive::Float64) => Some(TypeId::of::<f64>()),
_ => None,
}
}
pub fn is<T>(&self) -> bool
where
T: IsPrimitive + 'static,
{
TypeId::of::<T>()
== self
.type_id()
.expect("primitive types always have a type id")
}
}
fn write_packed_int(mut buffer: impl BufMut, mut value: u64) {
while value >= 0x80 {
buffer.put_u8((value & 0x7F) as u8 | 0x80);
value >>= 7;
}
buffer.put_u8(value as u8);
}
fn write_null_terminated_string(mut buffer: impl BufMut, string: impl AsRef<str>) {
buffer.put_slice(string.as_ref().as_bytes());
buffer.put_u8(0);
}
impl TypeRef<'_> {
pub fn size(&self) -> usize {
match self {
TypeRef::Primitive(Primitive::Void) => 0,
TypeRef::Primitive(Primitive::Bool) => 4,
TypeRef::Primitive(Primitive::Int32) => 4,
TypeRef::Primitive(Primitive::Int64) => 8,
TypeRef::Primitive(Primitive::Float32) => 4,
TypeRef::Primitive(Primitive::Float64) => 8,
TypeRef::String => 4,
TypeRef::Array(array) => array.size(),
TypeRef::Object(object) => object.size(),
}
}
pub fn to_owned(&self) -> Type {
match *self {
TypeRef::Primitive(primitive) => Type::Primitive(primitive),
TypeRef::String => Type::String,
TypeRef::Array(array) => Type::Array(Box::new(array.clone())),
TypeRef::Object(object) => Type::Object(Box::new(object.clone())),
}
}
pub(crate) fn serialise_as_choc_type(&self) -> Vec<u8> {
match self {
TypeRef::Primitive(Primitive::Void) => vec![0],
TypeRef::Primitive(Primitive::Int32) => vec![1],
TypeRef::Primitive(Primitive::Int64) => vec![2],
TypeRef::Primitive(Primitive::Float32) => vec![3],
TypeRef::Primitive(Primitive::Float64) => vec![4],
TypeRef::Primitive(Primitive::Bool) => vec![5],
TypeRef::String => todo!("serialising string types is not yet supported"),
TypeRef::Array(array) => {
let mut buffer = vec![];
buffer.put_u8(7);
buffer.put_u8(if array.is_empty() { 0 } else { 1 });
write_packed_int(&mut buffer, array.len() as u64);
buffer.put_slice(array.elem_ty().as_ref().serialise_as_choc_type().as_slice());
buffer
}
TypeRef::Object(object) => {
let mut buffer = vec![];
buffer.put_u8(8);
write_packed_int(&mut buffer, object.fields.len() as u64);
write_null_terminated_string(&mut buffer, object.class.as_str());
for field in object.fields() {
buffer.put_slice(field.ty().as_ref().serialise_as_choc_type().as_slice());
write_null_terminated_string(&mut buffer, field.name());
}
buffer
}
}
}
}
impl Array {
pub fn new(elem_ty: impl Into<Type>, len: usize) -> Self {
Array {
elem_ty: elem_ty.into(),
len,
}
}
pub fn size(&self) -> usize {
self.elem_ty.size() * self.len
}
pub fn elem_ty(&self) -> &Type {
&self.elem_ty
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl Object {
pub fn new(class: impl AsRef<str>) -> Self {
Object {
class: class.as_ref().to_string(),
fields: SmallVec::default(),
}
}
pub fn size(&self) -> usize {
self.fields.iter().map(|field| field.ty.size()).sum()
}
pub fn add_field(&mut self, name: impl AsRef<str>, ty: impl Into<Type>) {
let size = self.size();
self.fields.push(Field {
name: name.as_ref().to_owned(),
ty: ty.into(),
offset: size,
});
}
pub fn with_field(mut self, name: impl AsRef<str>, ty: impl Into<Type>) -> Self {
self.add_field(name, ty.into());
self
}
pub fn fields(&self) -> impl Iterator<Item = &Field> {
self.fields.iter()
}
}
impl From<Primitive> for Type {
fn from(primitive: Primitive) -> Self {
Type::Primitive(primitive)
}
}
impl From<Array> for Type {
fn from(array: Array) -> Self {
Type::Array(Box::new(array))
}
}
impl From<Object> for Type {
fn from(object: Object) -> Self {
Type::Object(Box::new(object))
}
}
impl Field {
pub fn name(&self) -> &str {
&self.name
}
pub fn ty(&self) -> &Type {
&self.ty
}
pub fn offset(&self) -> usize {
self.offset
}
}
#[sealed]
pub trait IsPrimitive {}
macro_rules! impl_is_primitive {
($($ty:ty),*) => {
$(
#[sealed]
impl IsPrimitive for $ty {}
)*
};
}
impl_is_primitive!(bool, i32, i64, f32, f64);
#[sealed]
pub trait IsScalar: IsPrimitive {}
macro_rules! impl_is_scalar {
($($ty:ty),*) => {
$(
#[sealed]
impl IsScalar for $ty {}
)*
};
}
impl_is_scalar!(i32, i64, f32, f64);
#[sealed]
pub trait IsFloatingPoint {}
#[sealed]
impl IsFloatingPoint for f32 {}
#[sealed]
impl IsFloatingPoint for f64 {}