cmajor/value/
types.rs

1//! Types of Cmajor values.
2
3use {
4    bytes::BufMut,
5    sealed::sealed,
6    serde::{Deserialize, Serialize},
7    smallvec::SmallVec,
8    std::any::TypeId,
9};
10
11/// A Cmajor type.
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub enum Type {
14    /// A primitive type.
15    Primitive(Primitive),
16
17    /// A string type.
18    String,
19
20    /// An array type.
21    Array(Box<Array>),
22
23    /// An object type.
24    Object(Box<Object>),
25}
26
27#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
28/// A Cmajor primitive.
29pub enum Primitive {
30    /// A void type.
31    Void,
32
33    /// A boolean type.
34    Bool,
35
36    /// A 32-bit signed integer type.
37    Int32,
38
39    /// A 64-bit signed integer type.
40    Int64,
41
42    /// A 32-bit floating-point type.
43    Float32,
44
45    /// A 64-bit floating-point type.
46    Float64,
47}
48
49/// A reference to a Cmajor [`Type`].
50#[derive(Debug, Copy, Clone, PartialEq)]
51pub enum TypeRef<'a> {
52    /// A primitive type.
53    Primitive(Primitive),
54
55    /// A string type.
56    String,
57
58    /// An array type.
59    Array(&'a Array),
60
61    /// An object type.
62    Object(&'a Object),
63}
64
65/// An object type.
66#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
67pub struct Object {
68    class: String,
69    fields: SmallVec<[Field; 2]>,
70}
71
72/// A field of an [`Object`].
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct Field {
75    name: String,
76    ty: Type,
77    offset: usize,
78}
79
80/// An array type.
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct Array {
83    elem_ty: Type,
84    len: usize,
85}
86
87impl Type {
88    /// The size of the type in bytes.
89    pub fn size(&self) -> usize {
90        self.as_ref().size()
91    }
92
93    /// Get a reference to the type.
94    pub fn as_ref(&self) -> TypeRef<'_> {
95        match self {
96            Type::Primitive(primitive) => TypeRef::Primitive(*primitive),
97            Type::String => TypeRef::String,
98            Type::Array(array) => TypeRef::Array(array.as_ref()),
99            Type::Object(object) => TypeRef::Object(object.as_ref()),
100        }
101    }
102
103    /// If the type is an object, return it.
104    pub fn as_object(&self) -> Option<&Object> {
105        match self {
106            Type::Object(object) => Some(object),
107            _ => None,
108        }
109    }
110
111    /// Returns the corresponding [`TypeId`] for the type (if any).
112    pub(crate) fn type_id(&self) -> Option<TypeId> {
113        match self {
114            Type::Primitive(Primitive::Void) => Some(TypeId::of::<()>()),
115            Type::Primitive(Primitive::Bool) => Some(TypeId::of::<bool>()),
116            Type::Primitive(Primitive::Int32) => Some(TypeId::of::<i32>()),
117            Type::Primitive(Primitive::Int64) => Some(TypeId::of::<i64>()),
118            Type::Primitive(Primitive::Float32) => Some(TypeId::of::<f32>()),
119            Type::Primitive(Primitive::Float64) => Some(TypeId::of::<f64>()),
120            _ => None,
121        }
122    }
123
124    /// Check whether the type is a given primitive.
125    pub fn is<T>(&self) -> bool
126    where
127        T: IsPrimitive + 'static,
128    {
129        TypeId::of::<T>()
130            == self
131                .type_id()
132                .expect("primitive types always have a type id")
133    }
134}
135
136fn write_packed_int(mut buffer: impl BufMut, mut value: u64) {
137    while value >= 0x80 {
138        buffer.put_u8((value & 0x7F) as u8 | 0x80);
139        value >>= 7;
140    }
141    buffer.put_u8(value as u8);
142}
143
144fn write_null_terminated_string(mut buffer: impl BufMut, string: impl AsRef<str>) {
145    buffer.put_slice(string.as_ref().as_bytes());
146    buffer.put_u8(0);
147}
148
149impl TypeRef<'_> {
150    /// The size of the type in bytes.
151    pub fn size(&self) -> usize {
152        match self {
153            TypeRef::Primitive(Primitive::Void) => 0,
154            TypeRef::Primitive(Primitive::Bool) => 4,
155            TypeRef::Primitive(Primitive::Int32) => 4,
156            TypeRef::Primitive(Primitive::Int64) => 8,
157            TypeRef::Primitive(Primitive::Float32) => 4,
158            TypeRef::Primitive(Primitive::Float64) => 8,
159            TypeRef::String => 4,
160            TypeRef::Array(array) => array.size(),
161            TypeRef::Object(object) => object.size(),
162        }
163    }
164
165    /// Convert the type reference into an owned [`Type`].
166    pub fn to_owned(&self) -> Type {
167        match *self {
168            TypeRef::Primitive(primitive) => Type::Primitive(primitive),
169            TypeRef::String => Type::String,
170            TypeRef::Array(array) => Type::Array(Box::new(array.clone())),
171            TypeRef::Object(object) => Type::Object(Box::new(object.clone())),
172        }
173    }
174
175    pub(crate) fn serialise_as_choc_type(&self) -> Vec<u8> {
176        match self {
177            TypeRef::Primitive(Primitive::Void) => vec![0],
178            TypeRef::Primitive(Primitive::Int32) => vec![1],
179            TypeRef::Primitive(Primitive::Int64) => vec![2],
180            TypeRef::Primitive(Primitive::Float32) => vec![3],
181            TypeRef::Primitive(Primitive::Float64) => vec![4],
182            TypeRef::Primitive(Primitive::Bool) => vec![5],
183            TypeRef::String => todo!("serialising string types is not yet supported"),
184            TypeRef::Array(array) => {
185                let mut buffer = vec![];
186                buffer.put_u8(7);
187                buffer.put_u8(if array.is_empty() { 0 } else { 1 });
188                write_packed_int(&mut buffer, array.len() as u64);
189                buffer.put_slice(array.elem_ty().as_ref().serialise_as_choc_type().as_slice());
190                buffer
191            }
192            TypeRef::Object(object) => {
193                let mut buffer = vec![];
194                buffer.put_u8(8);
195                write_packed_int(&mut buffer, object.fields.len() as u64);
196                write_null_terminated_string(&mut buffer, object.class.as_str());
197                for field in object.fields() {
198                    buffer.put_slice(field.ty().as_ref().serialise_as_choc_type().as_slice());
199                    write_null_terminated_string(&mut buffer, field.name());
200                }
201                buffer
202            }
203        }
204    }
205}
206
207impl Array {
208    /// Create a new array type.
209    pub fn new(elem_ty: impl Into<Type>, len: usize) -> Self {
210        Array {
211            elem_ty: elem_ty.into(),
212            len,
213        }
214    }
215
216    /// The size of the array in bytes.
217    pub fn size(&self) -> usize {
218        self.elem_ty.size() * self.len
219    }
220
221    /// The type of the array's elements.
222    pub fn elem_ty(&self) -> &Type {
223        &self.elem_ty
224    }
225
226    /// The number of elements in the array.
227    pub fn len(&self) -> usize {
228        self.len
229    }
230
231    /// Whether the array is empty.
232    pub fn is_empty(&self) -> bool {
233        self.len == 0
234    }
235}
236
237impl Object {
238    /// Create a new object type.
239    pub fn new(class: impl AsRef<str>) -> Self {
240        Object {
241            class: class.as_ref().to_string(),
242            fields: SmallVec::default(),
243        }
244    }
245
246    /// The size of the object in bytes.
247    pub fn size(&self) -> usize {
248        self.fields.iter().map(|field| field.ty.size()).sum()
249    }
250
251    /// Add a [`Field`] to the object.
252    pub fn add_field(&mut self, name: impl AsRef<str>, ty: impl Into<Type>) {
253        let size = self.size();
254        self.fields.push(Field {
255            name: name.as_ref().to_owned(),
256            ty: ty.into(),
257            offset: size,
258        });
259    }
260
261    /// Add a [`Field`] to the object.
262    pub fn with_field(mut self, name: impl AsRef<str>, ty: impl Into<Type>) -> Self {
263        self.add_field(name, ty.into());
264        self
265    }
266
267    /// The fields of the object.
268    pub fn fields(&self) -> impl Iterator<Item = &Field> {
269        self.fields.iter()
270    }
271}
272
273impl From<Primitive> for Type {
274    fn from(primitive: Primitive) -> Self {
275        Type::Primitive(primitive)
276    }
277}
278
279impl From<Array> for Type {
280    fn from(array: Array) -> Self {
281        Type::Array(Box::new(array))
282    }
283}
284
285impl From<Object> for Type {
286    fn from(object: Object) -> Self {
287        Type::Object(Box::new(object))
288    }
289}
290
291impl Field {
292    /// The name of the field.
293    pub fn name(&self) -> &str {
294        &self.name
295    }
296
297    /// The type of the field.
298    pub fn ty(&self) -> &Type {
299        &self.ty
300    }
301
302    /// The offset of the field in the object.
303    pub fn offset(&self) -> usize {
304        self.offset
305    }
306}
307
308/// Implemented for primitive types.
309#[sealed]
310pub trait IsPrimitive {}
311
312macro_rules! impl_is_primitive {
313    ($($ty:ty),*) => {
314        $(
315            #[sealed]
316            impl IsPrimitive for $ty {}
317        )*
318    };
319}
320
321impl_is_primitive!(bool, i32, i64, f32, f64);
322
323/// Implemented for scalar types.
324#[sealed]
325pub trait IsScalar: IsPrimitive {}
326
327macro_rules! impl_is_scalar {
328    ($($ty:ty),*) => {
329        $(
330            #[sealed]
331            impl IsScalar for $ty {}
332        )*
333    };
334}
335
336impl_is_scalar!(i32, i64, f32, f64);
337
338/// Implemented for floating point types.
339#[sealed]
340pub trait IsFloatingPoint {}
341
342#[sealed]
343impl IsFloatingPoint for f32 {}
344
345#[sealed]
346impl IsFloatingPoint for f64 {}