spacetimedb_sats/
bsatn.rs

1use crate::buffer::{BufReader, BufWriter, CountWriter};
2use crate::de::{BasicSmallVecVisitor, Deserialize, DeserializeSeed, Deserializer as _};
3use crate::ser::Serialize;
4use crate::{ProductValue, Typespace, WithTypespace};
5use ser::BsatnError;
6use smallvec::SmallVec;
7
8pub mod de;
9pub mod eq;
10pub mod ser;
11
12pub use de::Deserializer;
13pub use ser::Serializer;
14
15pub use crate::buffer::DecodeError;
16pub use ser::BsatnError as EncodeError;
17
18/// Serialize `value` into the buffered writer `w` in the BSATN format.
19#[inline]
20pub fn to_writer<W: BufWriter, T: Serialize + ?Sized>(w: &mut W, value: &T) -> Result<(), EncodeError> {
21    value.serialize_into_bsatn(Serializer::new(w))
22}
23
24/// Serialize `value` into a `Vec<u8>` in the BSATN format.
25pub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, EncodeError> {
26    let mut v = Vec::new();
27    to_writer(&mut v, value)?;
28    Ok(v)
29}
30
31/// Computes the size of `val` when BSATN encoding without actually encoding.
32pub fn to_len<T: Serialize + ?Sized>(value: &T) -> Result<usize, EncodeError> {
33    let mut writer = CountWriter::default();
34    to_writer(&mut writer, value)?;
35    Ok(writer.finish())
36}
37
38/// Deserialize a `T` from the BSATN format in the buffered `reader`.
39pub fn from_reader<'de, T: Deserialize<'de>>(reader: &mut impl BufReader<'de>) -> Result<T, DecodeError> {
40    T::deserialize(Deserializer::new(reader))
41}
42
43/// Deserialize a `T` from the BSATN format in `bytes`.
44pub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, DecodeError> {
45    from_reader(&mut &*bytes)
46}
47
48/// Decode `bytes` to the value type of `ty: S`.
49pub fn decode<'a, 'de, S: ?Sized>(
50    ty: &'a S,
51    bytes: &mut impl BufReader<'de>,
52) -> Result<<WithTypespace<'a, S> as DeserializeSeed<'de>>::Output, DecodeError>
53where
54    WithTypespace<'a, S>: DeserializeSeed<'de>,
55{
56    crate::WithTypespace::empty(ty).deserialize(Deserializer::new(bytes))
57}
58
59macro_rules! codec_funcs {
60    ($ty:ty) => {
61        impl $ty {
62            pub fn decode<'a>(bytes: &mut impl BufReader<'a>) -> Result<Self, DecodeError> {
63                from_reader(bytes)
64            }
65
66            pub fn encode(&self, bytes: &mut impl BufWriter) {
67                to_writer(bytes, self).unwrap()
68            }
69        }
70    };
71    (val: $ty:ty) => {
72        impl $ty {
73            /// Decode a value from `bytes` typed at `ty`.
74            pub fn decode<'a>(
75                ty: &<Self as crate::Value>::Type,
76                bytes: &mut impl BufReader<'a>,
77            ) -> Result<Self, DecodeError> {
78                decode(ty, bytes)
79            }
80
81            /// Decode a vector of values from `bytes` with each value typed at `ty`.
82            pub fn decode_smallvec<'a>(
83                ty: &<Self as crate::Value>::Type,
84                bytes: &mut impl BufReader<'a>,
85            ) -> Result<SmallVec<[Self; 1]>, DecodeError> {
86                Deserializer::new(bytes).deserialize_array_seed(
87                    BasicSmallVecVisitor,
88                    crate::WithTypespace::new(&Typespace::new(Vec::new()), ty),
89                )
90            }
91
92            pub fn encode(&self, bytes: &mut impl BufWriter) {
93                to_writer(bytes, self).unwrap()
94            }
95        }
96    };
97}
98
99codec_funcs!(crate::AlgebraicType);
100codec_funcs!(crate::ProductType);
101codec_funcs!(crate::SumType);
102codec_funcs!(crate::ProductTypeElement);
103codec_funcs!(crate::SumTypeVariant);
104
105codec_funcs!(val: crate::AlgebraicValue);
106codec_funcs!(val: crate::ProductValue);
107codec_funcs!(val: crate::SumValue);
108
109/// Types that can be encoded to BSATN.
110///
111/// Implementations of this trait may be more efficient than directly calling [`to_vec`].
112/// In particular, for `spacetimedb_table::table::RowRef`, this method will use a `StaticLayout` if one is available,
113/// avoiding expensive runtime type dispatch.
114pub trait ToBsatn {
115    /// BSATN-encode the row referred to by `self` into a freshly-allocated `Vec<u8>`.
116    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError>;
117
118    /// BSATN-encode the row referred to by `self` into `buf`,
119    /// pushing `self`'s bytes onto the end of `buf`, similar to [`Vec::extend`].
120    fn to_bsatn_extend(&self, buf: &mut Vec<u8>) -> Result<(), BsatnError>;
121
122    /// Returns the static size of the type of this object.
123    ///
124    /// When this returns `Some(_)` there is also a `StaticLayout`.
125    fn static_bsatn_size(&self) -> Option<u16>;
126}
127
128impl<T: ToBsatn> ToBsatn for &T {
129    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {
130        T::to_bsatn_vec(*self)
131    }
132    fn to_bsatn_extend(&self, buf: &mut Vec<u8>) -> Result<(), BsatnError> {
133        T::to_bsatn_extend(*self, buf)
134    }
135    fn static_bsatn_size(&self) -> Option<u16> {
136        T::static_bsatn_size(*self)
137    }
138}
139
140impl ToBsatn for ProductValue {
141    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {
142        to_vec(self)
143    }
144
145    fn to_bsatn_extend(&self, buf: &mut Vec<u8>) -> Result<(), BsatnError> {
146        to_writer(buf, self)
147    }
148
149    fn static_bsatn_size(&self) -> Option<u16> {
150        None
151    }
152}
153
154mod private_is_primitive_type {
155    pub trait Sealed {}
156}
157/// A primitive type.
158/// This is purely intended for use in `crates/bindings-macro`.
159///
160/// # Safety
161///
162/// Implementing this guarantees that the type has no padding, recursively.
163#[doc(hidden)]
164pub unsafe trait IsPrimitiveType: private_is_primitive_type::Sealed {}
165macro_rules! is_primitive_type {
166    ($($prim:ty),*) => {
167        $(
168            impl private_is_primitive_type::Sealed for $prim {}
169            // SAFETY:  the type is primitive and has no padding.
170            unsafe impl IsPrimitiveType for $prim {}
171        )*
172    };
173}
174is_primitive_type!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64);
175
176/// Enforces that a type is a primitive.
177/// This is purely intended for use in `crates/bindings-macro`.
178pub const fn assert_is_primitive_type<T: IsPrimitiveType>() {}
179
180#[cfg(test)]
181mod tests {
182    use super::{to_vec, DecodeError};
183    use crate::proptest::generate_typed_value;
184    use crate::{meta_type::MetaType, AlgebraicType, AlgebraicValue};
185    use proptest::prelude::*;
186    use proptest::proptest;
187
188    #[test]
189    fn type_to_binary_equivalent() {
190        check_type(&AlgebraicType::meta_type());
191    }
192
193    #[track_caller]
194    fn check_type(ty: &AlgebraicType) {
195        let mut through_value = Vec::new();
196        ty.as_value().encode(&mut through_value);
197        let mut direct = Vec::new();
198        ty.encode(&mut direct);
199        assert_eq!(direct, through_value);
200    }
201
202    proptest! {
203        #[test]
204        fn bsatn_enc_de_roundtrips((ty, val) in generate_typed_value()) {
205            let bytes = to_vec(&val).unwrap();
206            let val_decoded = AlgebraicValue::decode(&ty, &mut &bytes[..]).unwrap();
207            prop_assert_eq!(val, val_decoded);
208        }
209
210        #[test]
211        fn bsatn_non_zero_one_u8_aint_bool(val in 2u8..) {
212            let bytes = [val];
213            prop_assert_eq!(
214                AlgebraicValue::decode(&AlgebraicType::Bool, &mut &bytes[..]),
215                Err(DecodeError::InvalidBool(val))
216            );
217        }
218    }
219}